;-----------------------------------------------------------------------------
; Mini 8085 BIOS
; Mike Christle (c) 2014
;-----------------------------------------------------------------------------
;                        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.
;-----------------------------------------------------------------------------

#include "BIOS_IF.asm"

;=======================================================================
            ORG   00H         ;RST 0 - Reset or Timer 1
            DI
            JMP   INIT

            ORG   08H         ;RST 1 - Timer 2 or Port Int
            PUSH  H
            LHLD  T2VECT
            JMP   VECTOR

            ORG   10H         ;RST 2 - BIOS SW Functions
            JMP   BIOS

            ORG   18H         ;RST 3 - Timer 3/5
            PUSH  H
            LHLD  T3VECT
            JMP   VECTOR

            ORG   20H         ;RST 4 - Serial Receive
            JMP   SERRX

            ORG   28H         ;RST 5 - Serial Transmit
            JMP   SERTX

            ORG   30H         ;RST 6 - Timer 2/4
            PUSH  H
            LHLD  T4VECT
            JMP   VECTOR

            ORG   38H         ;RST 7 - Timer 5 or Port 2
            PUSH  H
            LHLD  T2VECT

VECTOR      CALL  VECJMP
            POP   H
            RET

VECJMP      PCHL              ;Jump to timer routine

;=======================================================================
;=======================================================================
            ORG   50H

INIT        DI
            LXI   SP, 0F000H

; MUART CMD1
;    BIT 0  FRQ = 0 - 16KHz
;    BIT 1 8086 = 0 - 8085
;    BIT 2 BITI = 0 - CT2 -> Priority 1
;    BIT 3 BRKI = 0 - Break-In Disabled
;    BIT 4:5 Sx = 0 - Stop Bit Length = 1
;    BIT 6:7 Lx = 0 - Char Length = 8

            MVI   A, 00000000B
            OUT   MCMD1

; MUART CMD2
;    BIT 0:3 Bx = 4 - Baud Rate 9600
;    BIT 4:5 Cx = 1 - Clock - 3.072 MHz
;    BIT 6   EP = 0 - No Parity
;    BIT 7  PEN = 0 - No Parity

            MVI   A, 00010100B
            OUT   MCMD2

; MUART CMD3
;    BIT 0  RST = 1 - Reset MUART
;    BIT 1 TBRK = 0 - No Break
;    BIT 2 SBRK = 0 - No Single Break
;    BIT 3  END = 0 - No Interrupts
;    BIT 4  NIE = 0 - Normal Interrupt Mode
;    BIT 5  IAE = 1 - Enable Interrupt Acknowledge
;    BIT 6  RxE = 1 - Enable Serial Receiver
;    BIT 7  SET = X - Set Bits

            MVI   A, 11100001B
            OUT   MCMD3
            MVI   A, 00011110B
            OUT   MCMD3

; MUART MODE
;    BIT 0:2 P2Cx = 0 - Port 2 Input Mode
;    BIT 3    CT2 = 0 - Timer 2 Mode - Timer
;    BIT 4    CT3 = 0 - Timer 3 Mode - Timer
;    BIT 5    T5C = 0 - Timer 5 Internal Trigger
;    BIT 6    T24 = 0 - Not Cascaded
;    BIT 7    T25 = 0 - Not Cascaded

            MVI   A, 00000000B
            OUT   MMODE

; MUART PORT 1 CONTROL
;    BIT 0:7 P1x = 0 - All Inputs

            MVI   A, 0
            OUT   MPORT1  ;DEBUG
            MVI   A, 255  ;debug
            OUT   MP1CTL
            
; MUART INTERRUPT ENABLE
;    BIT 0   L0 = 0 - Disable Timer 1 Interrupt
;    BIT 1   L1 = 0 - Disable Timer 2 Interrupt
;    BIT 2   L2 = 0 - Disable External Interrupt
;    BIT 3   L3 = 0 - Disable Timer 3 Interrupt
;    BIT 4   L4 = 1 - Enable Serial Receive Interrupt 
;    BIT 5   L5 = 1 - Enable Serial Transmit Interrupt
;    BIT 6   L6 = 0 - Disable Timer 4 Interrupt
;    BIT 7   L7 = 0 - Disable Timer 5 Interrupt

            MVI   A, 30H
            OUT   MINTEN

; Reset Serial Bus Buffers

            MVI   A, 0
            STA   RXTOP
            STA   RXBOT
            STA   TXTOP
            STA   TXBOT

; Setup serial port interrupt vectors

            LXI   H, SERRX
            SHLD  RXVECT
            LXI   H, SERTX
            SHLD  TXVECT    

; Clear timer interrupt vectors

            MVI   A, 0
            STA   T2VECT
            STA   T3VECT
            STA   T4VECT
            STA   T5VECT
            
;=======================================================================
; Enable interrupts and enter command loop
;=======================================================================

            EI
            LXI   B, HDRMSG
            CALL  PUTS

CMLOOP      LXI   B, PROMPT   ;Display prompt
            CALL  PUTS

            MVI   E, 0        ;Input a command
            LXI   B, CMDBUF
            CALL  GETS

            LDA   CMDBUF      ;Get first character
            ORI   20H         ;Convert to lower case

            CPI   ':'         ;Load RAM
            JNZ   CMLOOP1
            CALL  DOWNLOAD
            JMP   CMLOOP

CMLOOP1     CPI   's'         ;Start application routine
            JNZ   CMLOOP2
            CALL  8000H       ;Call application routine
            JMP   CMLOOP

CMLOOP2     CPI   'd'         ;Dump memory
            JNZ   CMLOOP3
            CALL  DUMPMEM
            JMP   CMLOOP

CMLOOP3     LXI   B, MENUMS   ;Default, display menu
            CALL  PUTS
            JMP   CMLOOP

HDRMSG      DB    'Mini 8085 Monitor", CHARCR, 0
PROMPT      DB    '>>> ", 0
MENUMS      DB    'S - Start Application', CHARCR
            DB    'D - Dump Memory Block', CHARCR
            DB    ': - Load RAM from HEX file', CHARCR, 0

;=======================================================================
; DOWNLOAD  HL - Load address
;           BC - CMDBUF pointer
;           D  - Byte count
;           E  - Checksum
;=======================================================================
DOWNLOAD    LXI   B, CMDBUF   ;Setup record pointer
            INX   B           ;Skip ':'

            CALL  HTOI        ;Get the record length
            MOV   D, A

            MOV   E, A        ;Init the checksum

            CALL  HTOI        ;Get offset high byte
            MOV   H, A

            ADD   E           ;Update the checksum
            MOV   E, A

            CALL  HTOI        ;Get offset low byte
            MOV   L, A

            ADD   E           ;Update the checksum
            MOV   E, A

            CALL  HTOI        ;Get record type
            ANA   A           ;Check data record type
            JNZ   CMLOD2      ;Ignore non-data records

CMLOD1      CALL  HTOI        ;Get a data byte
            MOV   M, A        ;Store in memory
            INX   H           ;Increment pointer

            ADD   E           ;Update the checksum
            MOV   E, A

            DCR   D           ;Decrement data count
            JNZ   CMLOD1      ;Loop until count zero

            CALL  HTOI        ;Get checksum byte
            ADD   E           ;Update the checksum
            JZ    CMLOD2      ;Continue if checksum is zero

            LXI   B, LODER1
            CALL  PUTS        ;Output error message

CMLOD2      RET

LODER1      DB    'Error - Bad checksum', CHARCR, 0

;=======================================================================
; HTOI      Hex to Integer
; Input     BC - Pointer to string
; Output    BC - Points to next character
;           A  - Value
;=======================================================================
HTOI        PUSH  D
            LDAX  B           ;Get a character from buffer
            INX   B           ;Increment buffer pointer

            SUI   '0'         ;Subtract '0'
            CPI   10          ;Compare to 10
            JC    HTOI1       ;Skip if less than 10
            SUI   7           ;Subtract 7 for letters

HTOI1       ADD   A           ;Shift left
            ADD   A           ;Shift left
            ADD   A           ;Shift left
            ADD   A           ;Shift left
            MOV   D, A        ;Save in D

            LDAX  B           ;Get a character from buffer
            INX   B           ;Increment buffer pointer

            SUI   '0'         ;Subtract '0'
            CPI   10          ;Compare to 10
            JC    HTOI2       ;Skip if less than 10
            SUI   7           ;Subtract 7 for letters

HTOI2       ORA   D           ;Or nibbles together
            POP   D
            RET

;=======================================================================
; DUMPMEM   BC - Memory address
;           D  - Byte count
;           E  - Line count
;=======================================================================
DUMPMEM     LXI   B, 8000H
            MVI   E, 8

DUMPMEM1    MVI   D, 16
            MOV   A, B
            CALL  PRINTHEX
            MOV   A, C
            CALL  PRINTHEX

DUMPMEM2    MVI   A, ' '
            CALL  PUTC
            LDAX  B
            INX   B
            CALL  PRINTHEX
            DCR   D
            JNZ   DUMPMEM2

            MVI   A, CHARCR
            CALL  PUTC
            DCR   E
            JNZ   DUMPMEM1

            RET

;=======================================================================
; PRINTHEX  Interger to Hex
; Input     A - Value to print
;=======================================================================
PRINTHEX    PUSH  PSW
            RAR
            RAR
            RAR
            RAR
            CALL  HEXDIGIT
            POP   PSW
            CALL  HEXDIGIT
            RET

;=======================================================================
; HEXDIGIT  Print one hexidecimal digit
; Input     A - Value to print
;=======================================================================
HEXDIGIT    PUSH  D
            ANI   0FH
            LXI   H, HEXDIGITS
            MOV   E, A
            MVI   D, 0
            DAD   D
            POP   D
            MOV   A, M
            CALL  PUTC
            RET

HEXDIGITS   DB    '0123456789ABCDEF'

;=======================================================================
; Serial Data Receive Interrupt Routine
;=======================================================================
SERRX       PUSH  PSW
            PUSH  H
            PUSH  B

            IN    MSERTR      ;Read serial receive port
            MOV   B, A

            LDA   RXBOT       ;Load bottom index
            MOV   L, A        ;Save for compare
            LDA   RXTOP       ;Load top index
            MOV   C, A
            INR   A           ;Increment index
            CMP   L           ;compare to bottom index
            JZ    SERRX1      ;Don't save new top if buffer full

            STA   RXTOP       ;Save new top index
            MOV   A, B
            MVI   B, 0        ;Clear upper byte
            LXI   H, RXBUF    ;Load buffer address
            DAD   B           ;Add index to address
            MOV   M, A        ;Save data byte in buffer

SERRX1      POP   B
            POP   H
            POP   PSW
            EI
            RET

;=======================================================================
;  Getc     Input   None
;           Output  A = Char or 0
;=======================================================================
GETC        PUSH  H
            PUSH  D

            LDA   RXBOT       ;Get bottom index
            MOV   E, A        ;Save for address calc
            LDA   RXTOP       ;Get top index
            SUB   E           ;Compare top to bottom
            JZ    GETC2       ;If equal, exit

           
            LXI   H, RXBUF    ;Get buffer address
            MVI   D, 0        ;Clear HI byte
            DAD   D           ;Add index to buffer address
            MOV   D, M        ;Get a character

            MOV   A, E        ;Get bottom index
            INR   A           ;Increment bottom index
            STA   RXBOT       ;Save new bottom index

            MOV   A, D
            ORA   A           ;Clear Z to indicate valid data

GETC2       POP   D
            POP   H
            RET

;=======================================================================
;  Gets     Inputs  E  = Max Buffer Size
;                   BC = Destination Pointer
;           Outputs Null terminated string
;           Returns when CR is read
;           Ignores all control characters
;=======================================================================
GETS        DCR   E           ;Decrement char count
            JZ    GETS4       ;Exit if buffer full

GETS2       CALL  GETC        ;Get a char
            JZ    GETS2       ;Try again if no input 

GETS3       CPI   CHARCR      ;Exit if char is a CR
            JZ    GETS4

            CPI   CHARSP      ;Ignore control characters
            JC    GETS2

            STAX  B           ;Save is callers buffer
            INX   B           ;Increment buffer pointer
            JMP   GETS        ;Loop for next character

GETS4       XRA   A           ;Store 0 at end of buffer
            STAX  B
            RET

;=======================================================================
; Serial Data Transmit Interrupt Routine
;=======================================================================
SERTX       PUSH  PSW
            PUSH  H
            PUSH  B

            LDA   TXTOP       ;Load buffer top index
            MOV   B, A
            LDA   TXBOT       ;Load buffer bottom index
            CMP   B           ;Test for empty buffer
            JZ    SERTX0      ;Exit if no data in buffre

SERTX1      MOV   C, A        ;Save for address offset
            MVI   B, 0        ;Clear upper byte
            INR   A           ;Increment bottom index
            STA   TXBOT       ;Save bottom index
            LXI   H, TXBUF    ;Load buffer address
            DAD   B           ;Add index to address
            MOV   A, M        ;Get data byte from buffer
            OUT   MSERTR      ;Write byte to serial port

SERTX0      POP   B
            POP   H
            POP   PSW
            EI
            RET

;=======================================================================
; PUTC      Input   A = Char
;           Waits if buffer is full
;=======================================================================
PUTC        PUSH  H
            PUSH  D
            PUSH  B

            MOV   B, A        ;Save Char
            LDA   TXTOP       ;Get top index
            MOV   E, A
            MVI   D, 0
            LXI   H, TXBUF    ;Get buffer address
            DAD   D           ;Add index to buffer address
            INR   E           ;Increment index

PUTC1       LDA   TXBOT       ;Get bottom index
            SUB   E           ;Compare top to bottom index
            JZ    PUTC1       ;Wait if buffer is full

            DI
            MOV   M, B        ;Put character in buffer
            MOV   A, E
            STA   TXTOP       ;Save new top index

            IN    MSTAT
            ANI   30H
            CPI   30H         ;Are TRE and TBE both set
            JNZ   PUTC2

            CALL  SERTX

PUTC2       MOV   A, B
            POP   B
            POP   D
            POP   H
            EI
            RET

;=======================================================================
; PUTS      Inputs  BC = Source String Pointer
;           Outputs None
;=======================================================================
PUTS        LDAX  B
            CPI   0
            JZ    PUTS3

            CALL  PUTC
            INX   B
            JMP   PUTS

PUTS3       RET

;=======================================================================
;  Reset 
;=======================================================================
RESET       DI
            JMP   INIT

;=======================================================================
; RST 2 - BIOS SW Functions
;=======================================================================
BIOS        PUSH  D
            MVI   H, 0
            LXI   D, FNTAB    ;Get function table address
            DAD   D           ;Add index to address
            POP   D
            PCHL              ;Jump to function routine

FDUMMY      RET

;=======================================================================
FNTAB       JMP   RESET       ;  0    Reset 
            JMP   GETC        ;  3    Getc
            JMP   GETS        ;  6    Gets
            JMP   PUTC        ;  9    putc
            JMP   PUTS        ; 12    puts

;=======================================================================
            ORG   0FC00H

; Serial Bus Buffers
RXBUF       DS    0100H
TXBUF       DS    0100H
CMDBUF      DS    0100H

RXTOP       DS    1
RXBOT       DS    1
TXTOP       DS    1
TXBOT       DS    1

; Interrupt Vectors
RXVECT      DS    2
TXVECT      DS    2
T2VECT      DS    2
T3VECT      DS    2
T4VECT      DS    2
T5VECT      DS    2

