;-----------------------------------------------------------------------------
; Mini Z80 BIOS
; Mike Christle (c) 2016
;-----------------------------------------------------------------------------
;                        TERMS OF USE: MIT License
;-----------------------------------------------------------------------------
; Permission is hereby granted, free of charge, to any person obtaining a
; copy of this software and associated documentation files (the "Software"),
; to deal in the Software without restriction, including without limitation
; the rights to use, copy, modify, merge, publish, distribute, sublicense,
; and/or sell copies of the Software, and to permit persons to whom the
; Software is furnished to do so, subject to the following conditions:
; 
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
; DEALINGS IN THE SOFTWARE.
;-----------------------------------------------------------------------------

;-----------------------------------------------------------------------------
; 68681 DUART Register Port Addresses
;-----------------------------------------------------------------------------
DUART_MR1A        EQU   0
DUART_MR2A        EQU   0
DUART_SRA         EQU   1
DUART_CSRA        EQU   1
DUART_CRA         EQU   2
DUART_RHRA        EQU   3
DUART_THRA        EQU   3
DUART_IPCR        EQU   4
DUART_ACR         EQU   4
DUART_ISR         EQU   5
DUART_IMR         EQU   5
DUART_IVR         EQU   12
DUART_OPCR        EQU   13
DUART_SOPR        EQU   14
DUART_ROPR        EQU   15

;-----------------------------------------------------------------------------
; 68681 Interrupt Service Register Constants
;-----------------------------------------------------------------------------
INPORT_INT_BIT    EQU   7
BREAKB_INT_BIT    EQU   6
RXRDYB_INT_BIT    EQU   5
TXRDYB_INT_BIT    EQU   4
CTRRDY_INT_BIT    EQU   3
BREAKA_INT_BIT    EQU   2
RXRDYA_INT_BIT    EQU   1
TXRDYA_INT_BIT    EQU   0

;-----------------------------------------------------------------------------
; 68681 Command Register Constants
; Bit 7   - 0
; Bit 654 - Commands
; Bit 3   - Disable Transmiter
; Bit 2   - Enable Transmiter
; Bit 1   - Disable Receiver
; Bit 0   - Enable Receiver
;-----------------------------------------------------------------------------
CRA_RESET_MRA     EQU   10H
CRA_RESET_RCVR    EQU   20H
CRA_RESET_XMTR    EQU   30H
CRA_RESET_ERRORS  EQU   40H
CRA_RESET_BREAK   EQU   50H
CRA_START_BREAK   EQU   60H
CRA_STOP_BREAK    EQU   70H
CRA_DISABLE_XMTR  EQU   08H
CRA_ENABLE_XMTR   EQU   04H
CRA_DISABLE_RCVR  EQU   02H
CRA_ENABLE_RCVR   EQU   01H

ASCII_CR          EQU   13
ASCII_LF          EQU   10

;-----------------------------------------------------------------------------
; RAM 
;-----------------------------------------------------------------------------
APP_START         EQU   8000H
STACK_INIT        EQU   0FE00H
                  ORG   STACK_INIT

BUFFER_SIZE       EQU   80H
BUFFER_SIZE_MASK  EQU   BUFFER_SIZE - 1
TxBufr            DS    BUFFER_SIZE
RxBufr            DS    BUFFER_SIZE
CmndBufr          DS    BUFFER_SIZE

TxTop             DS    1
TxBot             DS    1
RxTop             DS    1
RxBot             DS    1

ISR_IMAGE         DS    1
IMR_IMAGE         DS    1

                  ORG   0FFF0H
TXRDYA_IV         DS    2           ;TxRDY AInterrupt Vector
RXRDYA_IV         DS    2           ;RxRDY A Interrupt Vector
BREAKA_IV         DS    2           ;Delta Break A Interrupt Vector
CTRRDY_IV         DS    2           ;Counter Ready Interrupt Vector
TXRDYB_IV         DS    2           ;TxRDY B Interrupt Vector
RXRDYB_IV         DS    2           ;RxRDY B Interrupt Vector
BREAKB_IV         DS    2           ;Delta Break B Interrupt Vector
INPORT_IV         DS    2           ;In Port Change Interrupt Vector

;-----------------------------------------------------------------------------

                  ORG   00H
                  JP    SYSTEM_RESET

                  ORG   08H
                  JP    (HL)

                  ORG   10H
                  JP    SYSTEM_RESET

                  ORG   18H
                  JP    SYSTEM_RESET

                  ORG   20H
                  JP    SYSTEM_RESET

                  ORG   28H
                  JP    SYSTEM_RESET

                  ORG   30H
                  JP    SYSTEM_RESET

;-----------------------------------------------------------------------------
; 68681 DUART Interrupt Service Routine
;-----------------------------------------------------------------------------
                  ORG   38H
                  EX    AF, AF'
                  EXX

                  IN    A, (DUART_ISR)
                  LD    B, A
                  LD    A, (IMR_IMAGE)
                  AND   B
                  LD    (ISR_IMAGE), A

                  BIT   RXRDYA_INT_BIT, A
                  JR    Z, INT_VECTOR_1
                  LD    HL, (RXRDYA_IV)
                  CALL  INT_VECTOR_10

INT_VECTOR_1      LD    A, (ISR_IMAGE)
                  BIT   TXRDYA_INT_BIT, A
                  JR    Z, INT_VECTOR_2
                  LD    HL, (TXRDYA_IV)
                  CALL  INT_VECTOR_10

INT_VECTOR_2      LD    A, (ISR_IMAGE)
                  BIT   BREAKA_INT_BIT, A
                  JR    Z, INT_VECTOR_3
                  LD    HL, (BREAKA_IV)
                  CALL  INT_VECTOR_10

INT_VECTOR_3      LD    A, (ISR_IMAGE)
                  BIT   CTRRDY_INT_BIT, A
                  JR    Z, INT_VECTOR_4
                  LD    HL, (CTRRDY_IV)
                  CALL  INT_VECTOR_10

INT_VECTOR_4      LD    A, (ISR_IMAGE)
                  BIT   TXRDYB_INT_BIT, A
                  JR    Z, INT_VECTOR_5
                  LD    HL, (TXRDYB_IV)
                  CALL  INT_VECTOR_10

INT_VECTOR_5      LD    A, (ISR_IMAGE)
                  BIT   RXRDYB_INT_BIT, A
                  JR    Z, INT_VECTOR_6
                  LD    HL, (RXRDYB_IV)
                  CALL  INT_VECTOR_10

INT_VECTOR_6      LD    A, (ISR_IMAGE)
                  BIT   BREAKB_INT_BIT, A
                  JR    Z, INT_VECTOR_7
                  LD    HL, (BREAKB_IV)
                  CALL  INT_VECTOR_10

INT_VECTOR_7      LD    A, (ISR_IMAGE)
                  BIT   INPORT_INT_BIT, A
                  JR    Z, INT_VECTOR_8
                  LD    HL, (INPORT_IV)
                  CALL  INT_VECTOR_10

INT_VECTOR_8      EXX
                  EX    AF, AF'
                  EI
                  RETI

INT_VECTOR_10     JP    (HL)

;-----------------------------------------------------------------------------
; System Initialization
;-----------------------------------------------------------------------------
SYSTEM_RESET      DI                      ;Disable interrupts
                  LD    SP, STACK_INIT    ;Init stack pointer
                  IM    0                 ;Auto-Vector Interrupts

                  LD    A, 0
                  LD    (TxTop), A
                  LD    (TxBot), A
                  LD    (RxTop), A
                  LD    (RxBot), A
                  
;-----------------------------------------------------------------------------
; Init Interrupt Vectors
;-----------------------------------------------------------------------------
                  LD    HL, SYSTEM_RESET
                  LD    (InPort_IV), HL
                  LD    (BreakB_IV), HL
                  LD    (RxRdyB_IV), HL
                  LD    (TxRdyB_IV), HL
                  LD    (CtrRdy_IV), HL
                  LD    (BreakA_IV), HL

                  LD    HL, RX_ISR
                  LD    (RxRdyA_IV), HL

                  LD    HL, TX_ISR
                  LD    (TxRdyA_IV), HL

;-----------------------------------------------------------------------------
; Initialize the 68681 DUART
;-----------------------------------------------------------------------------
                  LD    A, CRA_RESET_RCVR
                  OUT   (DUART_CRA), A
                  LD    A, CRA_RESET_XMTR
                  OUT   (DUART_CRA), A
                  LD    A, CRA_RESET_MRA
                  OUT   (DUART_CRA), A

                  LD    A, 0FFH
                  OUT   (DUART_IVR), A

;-----------------------------------------------------------------------------
; 68681 MR1A
; Bit 7   = 0   - No RTS Control
; Bit 6   = 0   - RxDRY Causes Interrupt
; Bit 5   = 0   - Char Error Mode
; Bit 432 = 100 - No Parity
; Bit 10  = 11  - 8 Data Bits
;-----------------------------------------------------------------------------
                  LD    A, 13H
                  OUT   (DUART_MR1A), A

;-----------------------------------------------------------------------------
; 68681 MR2A
; Bit 76   = 00   - Normal Mode
; Bit 5    = 0    - No TxRTS Control
; Bit 4    = 0    - No TxCTS Control
; Bit 3210 = 0111 - 1 Stop Bit
;-----------------------------------------------------------------------------
                  LD    A, 07H
                  OUT   (DUART_MR2A), A

;-----------------------------------------------------------------------------
; 68681 CSRA
; Bit 7654 = 1011 - Receiver 9600 Baud
; Bit 3210 = 1011 - Transmiter 9600 Baud
;-----------------------------------------------------------------------------
                  LD    A, 0BBH
                  OUT   (DUART_CSRA), A

;-----------------------------------------------------------------------------
; 68681 IMR
; Bit 7 = 0 - In Port Change Interrupt
; Bit 6 = 0 - Delta Break B Interrupt
; Bit 5 = 0 - RxRDY B Interrupt
; Bit 4 = 0 - TxRDY B Interrupt
; Bit 3 = 0 - Counter Ready Interrupt
; Bit 2 = 0 - Delta Break A Interrupt
; Bit 1 = 1 - RxRDY A Interrupt
; Bit 0 = 1 - TxRDY A Interrupt
;-----------------------------------------------------------------------------
                  LD    A, 0FFH
                  OUT   (DUART_IMR), A
                  LD    (IMR_IMAGE), A

;-----------------------------------------------------------------------------
; Enable UART Receiver
;-----------------------------------------------------------------------------
                  LD    A, CRA_ENABLE_RCVR
                  OUT   (DUART_CRA), A

;-----------------------------------------------------------------------------
; Command loop
;-----------------------------------------------------------------------------
                  EI
                  LD    DE, HeaderMsg
                  CALL  PUTS

CMND_LOOP
                  LD    DE, PromptMsg
                  CALL  PUTS

                  LD    DE, CmndBufr
                  LD    C, BUFFER_SIZE
                  CALL  GETS

                  LD    A, (CmndBufr)
                  OR    20H
                  CP    'd'
                  JR    NZ, CMND_LOOP_1
                  CALL  DUMP_MEM
                  JR    CMND_LOOP

CMND_LOOP_1       CP    's'
                  JR    NZ, CMND_LOOP_2
                  CALL  APP_START
                  JR    CMND_LOOP

CMND_LOOP_2       CP    ':'
                  JR    NZ, CMND_LOOP_3
                  CALL  DOWNLOAD
                  JR    CMND_LOOP

CMND_LOOP_3       CP    'f'
                  JR    NZ, CMND_LOOP
                  CALL  FILL_MEM
                  JR    CMND_LOOP

HeaderMsg         DB    "Mini Z80", ASCII_CR, 0
PromptMsg         DB    "> ", 0

;-----------------------------------------------------------------------------
; DUART Receive Interrupt Service Routine
;-----------------------------------------------------------------------------
RX_ISR
                  LD    A, (RxTop)
                  LD    B, A
                  LD    HL, RxBufr
                  ADD   A, L
                  LD    L, A
                  IN    A, (DUART_RHRA)
                  LD    (HL), A

                  LD    A, B
                  INC   A
                  AND   BUFFER_SIZE_MASK
                  LD    (RxTop), A

RX_ISR_X          RET

;-----------------------------------------------------------------------------
; DUART Transmit Interrupt Service Routine
;-----------------------------------------------------------------------------
TX_ISR            LD    A, (TxBot)
                  LD    HL, TxTop
                  CP    (HL)
                  JR    NZ, TX_ISR_1

                  LD    A, CRA_DISABLE_XMTR
                  OUT   (DUART_CRA), A
                  JP    TX_ISR_X

TX_ISR_1          LD    B, A
                  LD    HL, TxBufr
                  ADD   A, L
                  LD    L, A
                  LD    A, (HL)
                  OUT   (DUART_THRA), A
                  LD    A, B
                  INC   A
                  AND   BUFFER_SIZE_MASK
                  LD    (TxBot), A

TX_ISR_X          RET

;-----------------------------------------------------------------------------
; GETC
; Output    A = Char
;           z = 1 if buffer empty
;-----------------------------------------------------------------------------
GETC              PUSH  HL
                  PUSH  BC
                  LD    A, (RxBot)
                  LD    B, A
                  LD    HL, RxTop
                  SUB   (HL)
                  JR    Z, GETC_X

                  LD    A, B
                  LD    HL, RxBufr
                  ADD   A, L
                  LD    L, A
                  LD    A, B
                  INC   A
                  AND   BUFFER_SIZE_MASK
                  DI
                  LD    (RxBot), A
                  LD    A, (HL)
                  EI
                  BIT   7, H

GETC_X            POP   BC
                  POP   HL
                  RET

;-----------------------------------------------------------------------------
; GETS
; Exits on CR or buffer full
; Terminates buffer with 0.
; Input     DE = Address of buffer
;           C  = Buffer size
;-----------------------------------------------------------------------------
GETS              DEC   C
                  JR    Z, GETS_X

GETS_1            CALL  GETC
                  JR    Z, GETS_1

                  CP    ASCII_CR
                  JR    Z, GETS_X

                  LD    (DE), A
                  INC   DE
                  JR    GETS

GETS_X            XOR   A
                  LD    (DE), A
                  RET

;-----------------------------------------------------------------------------
; PUTC
; Waits if buffer full
; Input     A = Char
;-----------------------------------------------------------------------------
PUTC              PUSH  HL
                  PUSH  BC
                  PUSH  AF

                  LD    A, (TxTop)
                  LD    BC, TxBufr
                  ADD   A, C
                  LD    C, A
                  
                  LD    A, (TxTop)
                  INC   A
                  AND   BUFFER_SIZE_MASK

                  LD    HL, TxBot
PUTC_1            CP    (HL)
                  JR    Z, PUTC_1

                  DI
                  LD    (TxTop), A
                  POP   AF
                  LD    (BC), A
                  EI

                  LD    A, CRA_ENABLE_XMTR
                  OUT   (DUART_CRA), A

                  POP   BC
                  POP   HL
                  RET

;-----------------------------------------------------------------------------
; PUTS
; Input     DE = Address of null terminated string
;-----------------------------------------------------------------------------
PUTS              LD    A, (DE)
                  CP    0
                  JR    Z, PUTS_X

                  CALL  PUTC
                  INC   DE
                  JR    PUTS

PUTS_X            RET

;-----------------------------------------------------------------------------
; Dump Memory
; B  - Line Counter
; C  - Byte Counter
; DE - Memory Address
;-----------------------------------------------------------------------------
DUMP_MEM          LD    DE, APP_START
                  LD    B, 8

DUMP_MEM_1        LD    C, 16
                  LD    A, D
                  CALL  PRINT_HEX
                  LD    A, E
                  CALL  PRINT_HEX

DUMP_MEM_2        LD    A, ' '
                  CALL  PUTC

                  LD    A, (DE)
                  INC   DE
                  CALL  PRINT_HEX

                  DEC   C
                  JR    NZ, DUMP_MEM_2

                  LD    A, ASCII_CR
                  CALL  PUTC
                  DEC   B
                  JR    NZ, DUMP_MEM_1

DUMP_MEM_X        RET

;-----------------------------------------------------------------------------
; Print a Hex Digit
; Input     A = Value
;-----------------------------------------------------------------------------
PRINT_HEX         PUSH  HL
                  PUSH  AF
                  SRL   A
                  SRL   A
                  SRL   A
                  SRL   A

                  LD    HL, HEX_DIGITS
                  ADD   A, L
                  LD    L, A
                  LD    A, (HL)
                  CALL  PUTC

                  POP   AF
                  AND   0FH
                  LD    HL, HEX_DIGITS
                  ADD   A, L
                  LD    L, A
                  LD    A, (HL)
                  CALL  PUTC

                  POP   HL
                  RET

HEX_DIGITS        DB    "0123456789ABCDEF"

;-----------------------------------------------------------------------------
; FILL_MEM
;-----------------------------------------------------------------------------
FILL_MEM          LD    DE, APP_START
                  LD    A, 0

FILL_MEM_1        LD    (DE), A
                  INC   DE
                  INC   A
                  JR    NZ, FILL_MEM_1
                  RET


;-----------------------------------------------------------------------------
; DOWNLOAD
; HL - Load address
; BC - CMDBUF pointer
; D  - Byte count
; E  - Checksum
;-----------------------------------------------------------------------------
DOWNLOAD          LD    BC, CmndBufr      ;Setup record pointer
                  INC   BC                ;Skip ':'

                  CALL  HTOI              ;Get the record length
                  LD    D, A

                  LD    E, A              ;Init the checksum

                  CALL  HTOI              ;Get offset high byte
                  LD    H, A

                  ADD   A, E              ;Update the checksum
                  LD    E, A

                  CALL  HTOI              ;Get offset low byte
                  LD    L, A

                  ADD   A, E              ;Update the checksum
                  LD    E, A

                  CALL  HTOI              ;Get record type
                  AND   A                 ;Check data record type
                  JR    NZ, DOWNLOAD_2    ;Ignore non-data records

DOWNLOAD_1        CALL  HTOI              ;Get a data byte
                  LD    (HL), A           ;Store in memory
                  INC   HL                ;Increment pointer

                  ADD   A, E              ;Update the checksum
                  LD    E, A

                  DEC   D                 ;Decrement data count
                  JR    NZ, DOWNLOAD_1    ;Loop until count zero

                  CALL  HTOI              ;Get checksum byte
                  ADD   A, E              ;Update the checksum
                  JR    Z, DOWNLOAD_2     ;Continue if checksum is zero

                  LD    HL, DOWNLOAD_ERR_MSG
                  CALL  PUTS              ;Output error message

DOWNLOAD_2        RET

DOWNLOAD_ERR_MSG  DB    "Error - Bad checksum", ASCII_CR, 0

;-----------------------------------------------------------------------------
; HTOI      Hex to Integer
; Input     BC - Pointer to string
; Output    BC - Points to next character
;           A  - Value
;-----------------------------------------------------------------------------
HTOI              PUSH  DE
                  LD    A, (BC)           ;Get a character from buffer
                  INC   BC                ;Increment buffer pointer

                  SUB   '0'               ;Subtract '0'
                  CP    10                ;Compare to 10
                  JR    C, HTOI_1         ;Skip if less than 10
                  SUB   7                 ;Subtract 7 for letters

HTOI_1            SLA   A                 ;Shift left
                  SLA   A                 ;Shift left
                  SLA   A                 ;Shift left
                  SLA   A                 ;Shift left
                  LD    D, A              ;Save in D

                  LD    A, (BC)           ;Get a character from buffer
                  INC   BC                ;Increment buffer pointer

                  SUB   '0'               ;Subtract '0'
                  CP    10                ;Compare to 10
                  JR    C, HTOI_2         ;Skip if less than 10
                  SUB   7                 ;Subtract 7 for letters

HTOI_2            OR    D                 ;Combine nibbles together
                  POP   DE
                  RET

;-----------------------------------------------------------------------------
; Link Interrupt Service Routine
; A  = Vector Number
; DE = ISR Address
;-----------------------------------------------------------------------------
LINK_ISR          PUSH  HL
                  LD    HL, 0FFF0H
                  ADD   A, L
                  LD    L, A
                  LD    (HL), E
                  INC   HL
                  LD    (HL), D
                  POP   HL
                  RET

;-----------------------------------------------------------------------------
; BIOS Interface Vectors
;-----------------------------------------------------------------------------
                  ORG   1F00H
VECTOR_TABLE      DW    GETC
                  DW    PUTC
                  DW    GETS
                  DW    PUTS
                  DW    PRINT_HEX
                  DW    LINK_ISR
