TITLE    LED - Steuerung mit I2C 
.Z80
;*************************************************************
;*      Hobby, Z80/Z180
;*      einfache Programmierung
;*      Lernen Z80 Maschinensprache
;*************************************************************
;*      Kurt Pieper, Germany
;*      Version 15.03.2021
;*      Update  15.03.2021
;*************************************************************
;*      LED ansteuern Taste 0-8
;       SC22C Modul --> SC407
;*************************************************************
;*      Compiler: z80asm i2c8574
;**************************************************************
;       i2c8574.mac             ; umbenenen
;       M80 i2c8574=i2c8574/L
;       L80 i2c8574,i2c8574/N/E
;
; Die Benutung des Programmes erfolgt auf eigene Gefahr.
; Achtung!
; Diverse Programmteile (I2C Routinen) (c) by Steve Cousins
;
;    S e g u e n z e n
WARM          EQU     0H              ; CP/M warm start
BDOS          EQU     5H              ; oder FDOS
CONOUT        EQU     2H
DIRECT        EQU     6H
STRING        EQU     9H
LF            EQU     0AH
CR            EQU     0DH
ESC           EQU     1BH
CRLF          EQU     0D0AH
;DB  ESC,+'[','17';','37,'H'          ; row 17, col 37
;
;    Tasten zur LED Anzeige
ONE           EQU     31H
TWO           EQU     32H
THREE         EQU     33H
FOUR          EQU     34H
FIVE          EQU     35H
SIX           EQU     36H
SEVEN         EQU     37H
EIGHT         EQU     38H
ZERO          EQU     30H
;
; I2C Parameter 
I2C_PORT   EQU 020H           ;Host I2C port address
I2C_SDA_WR EQU 7              ;Host I2C write SDA bit number
I2C_SCL_WR EQU 0              ;Host I2C write SCL bit number
I2C_SDA_RD EQU 7              ;Host I2C read SDA bit number
I2C_ADDR   EQU 040H           ;I2C device addess
;
;             ASEG                ; wenn M80 !!!!
;
;    H A U P T P R O G R A M M
;****************************************************************
              ORG     0100H           ; TPA
;
START:        CALL    CLS             ; loesche Bildschirn
              CALL    HELLO           ; 1. Meldung
;
ENTRY:        LD      E,0FFH          ; Input a character using
              LD      C,DIRECT        ; Direct console I/O
              CALL    BDOS
              OR      A               ; Was a key pressed?
              JR      Z,ENTRY         ; If not, keep waiting
CRTEST:       CP      CR              ; Did they press <CR>?
              JR      Z,MAIN          ; If so, do your terminal stuff
;             CALL    BEEP            ; Else, signal error, led blink
              JR      ENTRY           ; and wait for another entry
;----------------------------------------------------------------
;
MAIN:         CALL    CLS
              LD      A,11000000b      ; SCL and SDA high + LED 1
              CALL    I2C_WrPort
              LD      A,I2C_ADDR       ; I2C address to write to
              CALL    I2C_Open         ; Open I2C device for write
              CALL    INFO_OPEN        ; Anzeige I2C-Port geoeffnet  
              CALL    MSGLED           ; 
;
LOOP:         JR      Z,KEYIN          ; check keyboard
              CP      ESC              ; Abfrage Minus [-] Nummernblock
              CP      'm'              ; ESC +'m'
              JP      Z,BYE            ; BYE -> CP/M
;
KEYIN:        CALL    KEYS             ; Taste ?, welches Zeichen
              JR      Z,LOOP           ; kein Zeichen gefunden, goto LOOP
              CP      ESC
              JP      Z,BYE            ; BYE -> CP/M
              CP      ONE              ; A =  ? Taste 1
              JP      Z,LED1           ; Ausgabe Port 040H, 11111110
              CP      TWO              ; war Taste 2
              JP      Z,LED2           ; Ausgabe Port 040H, 11111101
              CP      THREE            ; usw....
              JP      Z,LED3
              CP      FOUR
              JP      Z,LED4
              CP      FIVE
              JP      Z,LED5
              CP      SIX
              JP      Z,LED6
              CP      SEVEN
              JP      Z,LED7
              CP      EIGHT
              JP      Z,LED8
              CP      ZERO             ; 0
              JP      Z,LED0           ; Taste 0 LED loeschen
XX:           JR      LOOP              
;
LED1:         LD     A,11111110b
              CALL I2C_Write
              JP     XX
;
LED2:         LD     A,11111101b
              CALL I2C_Write
              JP     XX
;
LED3:         LD     A,11111011b
              CALL I2C_Write
              JP     XX
;
LED4:         LD     A,11110111b
              CALL I2C_Write
              JP     XX
;
LED5:         LD     A,11101111b 
              CALL I2C_Write
              JP     XX
;
LED6:         LD     A,11011111b 
              CALL I2C_Write
              JP     XX
;
LED7:         LD     A,10111111b 
              CALL I2C_Write
              JP     XX
;
LED8:         LD     A,01111111b 
              CALL I2C_Write
              JP     XX
;
LED0:         LD     A,11111111b 
              CALL I2C_Write
              JP     LOOP
;
KEYNO:        PUSH   AF
              CALL   MSGLED
              POP    AF
              JP     XX
;
; I2C bus open device
;   On entry: A = Device address (bit zero is read flag)
;             SCL = unknown, SDA = unknown
;   On exit:  If successfully A = 0 and Z flagged
;             If successfully A = Error and NZ flagged
;             SCL = lo, SDA = lo
;             HL IX IY preserved
; Possible errors:  1 = Bus jammed (not implemented)
I2C_Open:   PUSH AF
            CALL I2C_Start      ;Output start condition
            POP  AF
            JR   I2C_Write      ;Write data byte


; I2C bus close device
;   On entry: SCL = unknown, SDA = unknown
;   On exit:  If successfully A=0 and Z flagged
;             If successfully A=Error and NZ flagged
;             SCL = hi, SDA = hi
;             HL IX IY preserved
; Possible errors:  1 = Bus jammed ??????????
I2C_Close:  JR   I2C_Stop       ;Output stop condition


; I2C bus transmit frame (address or data)
;   On entry: A = Data byte, or
;                 Address byte (bit zero is read flag)
;             SCL = low, SDA = low
;   On exit:  If successful A=0 and Z flagged
;                SCL = lo, SDA = lo
;             If unsuccessful A=Error and NZ flagged
;                SCL = high, SDA = high, I2C closed
;             HL IX IY preserved
I2C_Write:  LD   D,A            ;Store byte to be written
            LD   B,8            ;8 data bits, bit 7 first
@Wr_Loop:   RL   D              ;Test M.S.Bit
            JR   C,@Bit_Hi      ;High, so skip
            CALL I2C_SDA_LO     ;SDA low   (SCL lo, SDA = data bit)
            JR   @Bit_Clk
@Bit_Hi:    CALL I2C_SDA_HI     ;SDA high  (SCL lo, SDA = data bit)
@Bit_Clk:   CALL I2C_SCL_HI     ;SCL high  (SCL hi, SDA = data bit)
            CALL I2C_SCL_LO     ;SCL low   (SCL lo, SDA = data bit)
            DJNZ @Wr_Loop
; Test for acknowledge from slave (receiver)
; On arriving here, SCL = lo, SDA = data bit
            CALL I2C_SDA_HI     ;SDA high  (SCL lo, SDA hi/ack)
            CALL I2C_SCL_HI     ;SCL high  (SCL hi, SDA hi/ack)
            CALL I2C_RdPort     ;Read SDA input
            LD   B,A
            CALL I2C_SCL_LO     ;SCL low   (SCL lo, SDA = hi)
            BIT  I2C_SDA_RD,B
            JR   NZ,@NoAck      ;Skip if no acknowledge
            XOR  A              ;Return success A=0 and Z flagged
            RET
; I2C STOP required as no acknowledge
; On arriving here, SCL = lo, SDA = hi
@NoAck:     CALL I2C_SDA_LO     ;SDA low   (SCL lo, SDA = lo)
            CALL I2C_SCL_HI     ;SCL high  (SCL hi, SDA = lo)
            CALL I2C_SDA_HI     ;SDA low   (SCL hi, SDA = hi)
            LD   A,2            ;Return error 2 - No Ack
            OR   A              ;  and NZ flagged
            RET


; I2C bus receive frame (data)
;   On entry: SCL low, SDA low
;   On exit:  If successful A = data byte and Z flagged
;               SCL = low, SDA = low
;             If unsuccessul A = Error and NZ flagged
;               SCL = low, SDA = low ??? no failures supported
;             HL IX IY preserved
I2C_Read:   LD   B,8            ;8 data bits, 7 first
            CALL I2C_SDA_HI     ;SDA high  (SCL lo, SDA hi/input)
@Rd_Loop:   CALL I2C_SCL_HI     ;SCL high  (SCL hi, SDA hi/input)
            CALL I2C_RdPort     ;Read SDA input bit
            SCF                 ;Set carry flag
            BIT  I2C_SDA_RD,A   ;SDA input high?
            JR   NZ,@Rotate     ;Yes, skip with carry flag set
            CCF                 ;Clear carry flag
@Rotate:    RL   D              ;Rotate result into D
            CALL I2C_SCL_LO     ;SCL low   (SCL lo, SDA hi/input)
            DJNZ @Rd_Loop       ;Repeat for all 8 bits
; Acknowledge input byte
; On arriving here, SCL = lo, SDA = hi/input
            CALL I2C_SDA_LO     ;SDA low   (SCL lo, SDA lo)
            CALL I2C_SCL_HI     ;SCL hi    (SCL hi, SDA lo)
            CALL I2C_SCL_LO     ;SCL low   (SCL lo, SDA lo)
            LD   A,D            ;Get data byte received
            CP   A              ;Return success Z flagged
            RET


; I2C bus start
;   On entry: SCL = unknown, SDA = unknown
;   On exit:  SCL = low, SDA = low
;             BC DE HL IX IY preserved
; First ensure SDA and SCL are high
I2C_Start:  CALL I2C_SCL_HI     ;SCL high  (SCL hi, SDA ??)
            CALL I2C_SDA_HI     ;SDA high  (SCL hi, SDA hi)
; Generate I2C start condition
            CALL I2C_SDA_LO     ;SDA low   (SCL hi, SDA lo)
            CALL I2C_SCL_LO     ;SCL low   (SCL lo, SDA lo)
            RET


; I2C bus stop 
;   On entry: SCL = unknown, SDA = unknown
;   On exit:  SCL = high, SDA = high
;             BC DE HL IX IY preserved
; First ensure SDA and SCL are low
I2C_Stop:   CALL I2C_SDA_LO     ;SDA low   (SCL hi, SDA lo)
            CALL I2C_SCL_LO     ;SCL low   (SCL lo, SDA lo)
; Generate stop condition
            CALL I2C_SCL_HI     ;SCL high  (SCL hi, SDA lo)
            CALL I2C_SDA_HI     ;SDA low   (SCL hi, SDA hi)
            RET


; **********************************************************************
; I2C bus simple I/O functions
;   On entry: No parameters required
;   On exit:  BC DE HL IX IY preserved

I2C_SCL_HI: LD   A,(I2C_RAMCPY)
            SET  I2C_SCL_WR,A
            JR   I2C_WrPort

I2C_SCL_LO: LD   A,(I2C_RAMCPY)
            RES  I2C_SCL_WR,A
            JR   I2C_WrPort

I2C_SDA_HI: LD   A,(I2C_RAMCPY)
            SET  I2C_SDA_WR,A
            JR   I2C_WrPort

I2C_SDA_LO: LD   A,(I2C_RAMCPY)
            RES  I2C_SDA_WR,A
            ;JR   I2C_WrPort

I2C_WrPort: PUSH BC
            LD   B,0            ;Set up BC for 16-bit
            LD   C,I2C_PORT     ;  I/O address of I2C port
            OUT  (C),A          ;Write A to I2C I/O port
            LD   (I2C_RAMCPY),A ;Write A to RAM copy
            POP  BC
            RET

I2C_RdPort: PUSH BC
            LD   B,0            ;Set up BC for 16-bit
            LD   C,I2C_PORT     ;  I/O address of I2C port
            IN   A,(C)          ;Read A from I/O port
            POP  BC
            RET

; **********************************************************************
; Small computer monitor API

; Delay by DE milliseconds (approx)
;   On entry: DE = Delay time in milliseconds
;   On exit:  IX IY preserved
API_Delay:  ;LD   C,0AH
            ;RST  030H
            ;RET

MSWAIT:     PUSH    BC
            LD      BC,1000/12    ; 1MS
MSLP1:      DEC     BC
            LD      A,B
            OR      C
            JP      NZ, MSLP1
            POP     BC
            RET
;
BYE:          CALL    INFO_CLOSE
              CALL    I2C_Close
              JP      WARM
;
CLS:          LD      E,0CH             ; ^L loesche Bildschirm
              LD      C,CONOUT
              CALL    BDOS
              RET
;
KEYS:         LD      E,0FFH            ; Direct input
              LD      C,DIRECT
              CALL    BDOS
              AND     A                 ; A = char
              RET
;
HELLO:        LD      C,STRING
              LD      DE,MSG1
              CALL    BDOS              ; erste Meldung
              RET
;
MSG1:         DB      1BH, '[','5',';','1','H'
              DB      '                        P R O G R A M M  ** L E D/I2C'
              DW      CRLF
              DW      CRLF
              DB      '                           Minicom  115200 Baud   '
              DW      CRLF
              DB      '                        dev/ttyUSB0:  115200,8,N,1'
              DW      CRLF
              DB      '                                SC203 - SC408   '
              DW      CRLF
              DW      CRLF
              DB      '                                Kurt Pieper        '
              DW      CRLF
              DB      '                             Version:  15.03.2021   '
              DW      CRLF
              DW      CRLF
              DB      '                          Press <RETURN> Eingabe'
              DW      CRLF
              DB      '                                    '
              DB      '$'

INFO_OPEN:    LD      C,STRING
              LD      DE,MSGOPEN
              CALL    BDOS
              RET
;
MSGOPEN:      DB      1BH, '[','5',';','1','H'
              DB      ' I2C PORT OPEN '
              DB      '$'             
;
INFO_CLOSE:   LD      A,11111111b  ; alle LED aus                                               
              CALL    I2C_Write
              LD      C,STRING
              LD      DE,MSGCLOSE
              CALL    BDOS
              RET
;
MSGCLOSE:     DB      1BH, '[','5',';','1','H'
              DB      ' I2C PORT CLOSE '
              DB      1BH, '[','17',';','1','H'
              DB      '$'
;
MSGLED:       LD      C,STRING
              LD      DE,MSG0
              CALL    BDOS
              LD      A,01H
              RET
;
MSG0:         DB      CR
              DB      1BH, '[','12',';','1','H'
              DB      ' Schalte L E D  (0,1 - 8 ESC = Ende '
              DB      '$'

; **********************************************************************
; Workspace / variable in RAM

I2C_RAMCPY: DB  0
