;-----------------------------------------------------------------------------
; Mini 6502 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.
;-----------------------------------------------------------------------------

UART_DATA       EQU $8000
UART_STATUS     EQU $8001
UART_COMMAND    EQU $8002
UART_CONTROL    EQU $8003

VIA_PORTB       EQU $8010
VIA_PORTA       EQU $801F
VIA_DDRB        EQU $8012
VIA_DDRA        EQU $8013

CR              EQU 13
LF              EQU 10

; UART Control Register
; Bit    7 = 0    - 1 Stop Bit
; Bit   65 = 00   - 8 Data Bits
; Bit    4 = 1    - Baud Rate Gen
; Bit 3210 = 1110 - 9600 Baud

UART_CNTRL_VAL  EQU $1E

; UART Command Register
; Bit 765 = 000 - No Parity
; Bit   4 = 0   - Receiver Normal Mode
; Bit  32 = 01  - RTS* Lo, Enable TX Interrupt
; Bit   1 = 0   - RX Interrupt Enabled
; Bit   0 = 1   - DTR* Lo

UART_CMND_EI    EQU $05
UART_CMND_DI    EQU $09

;---------------------------------------------------------------------
                ORG $00F0
VIA_IntVec      DS  2

MReg0           DS  1
MReg1           DS  1
MReg2           DS  1
MReg3           DS  1
MReg4           DS  1
MReg5           DS  1
MReg6           DS  1
MReg7           DS  1

Pointer         DS  1
PointerHI       DS  1

TXTop           DS  1
TXBot           DS  1
RXTop           DS  1
RXBot           DS  1

                ORG $0200
BUFFER_SIZE     EQU 256
TXBuf           DS  BUFFER_SIZE
RXBuf           DS  BUFFER_SIZE
CmndBuf         DS  BUFFER_SIZE

;---------------------------------------------------------------------
                ROMBASE $E000
                ORG $E000

;---------------------------------------------------------------------
MonStart        SEI                 ;Disable interrupts

                CLD                 ;Turn decimal mode off
                LDX #$FF            ;Init the stack pointer
                TXS

                LDA #0
                STA TXTop           ;Init serial data buffer pointers
                STA TXBot
                STA RXTop
                STA RXBot

                LDA #UART_CMND_EI
                STA UART_COMMAND
                LDA #UART_CNTRL_VAL ;Init UART
                STA UART_CONTROL

                CLI                 ;Enable Interrupts

                LDA #<HeaderMsg     ;Display header message
                LDY #>HeaderMsg
                JSR PutString

                JSR CmdLoop
StopLoop        JMP StopLoop

HeaderMsg       DB  "Mini 6502", CR, LF, 0

;---------------------------------------------------------------------
;Delay           LDA #0
;Delay1          DEC A
;                BNE Delay1
;                RTS

;---------------------------------------------------------------------
UART_Int        PHA
                TXA
                PHA

                LDA UART_STATUS     ;Get UART Status
                AND #$08            ;Test RX ready flag
                BEQ UART_Int1       ;Skip if no data ready

                LDA UART_DATA       ;Get data byte
                LDX RXTop           ;Get top offset
                STA RXBuf, X        ;Store byte in RX buffer
                INX                 ;Increment top pointer
                STX RXTop           ;Save incremented offset

UART_Int1       LDA UART_STATUS     ;Get UART Status
;                sta VIA_PORTA       ;debug
                AND #$10            ;Test TX empty flag
                BEQ UART_IntX       ;Exit if output buffer full

                LDX TXBot           ;Get TX bottom offset
                CPX TXTop           ;Compare to top offset
                BEQ UART_IntX       ;Continue if data ready

                LDA TXBuf, X        ;Get next data byte
                STA UART_DATA       ;Send to UART
                INX                 ;Increment offset
                STX TXBot           ;Save new bottom offset

UART_IntX       PLA
                TAX
                PLA
                RTI

;---------------------------------------------------------------------
; GetChar
;
; Output    A = Data Byte
;           Z = 1 if buffer empty
;---------------------------------------------------------------------
GetChar         LDX RXBot           ;Get bottom offset
                CPX RXTop           ;Compare to top offset
                BEQ GetCharX        ;Exit if offsets equal

                LDA RXBuf, X        ;Get byte from buffer
                INX                 ;Increment offset
                STX RXBot           ;Save new offset
                CMP #0              ;Set success flag

GetCharX        RTS

;---------------------------------------------------------------------
; PutChar
; Waits if buffer is full.
; Input     A = Data Byte
;---------------------------------------------------------------------
PutChar         LDX TXTop           ;Get TX top offset
                INX                 ;Increment top offset
                CPX TXBot           ;Compare to bottom offset
                BEQ PutChar         ;Wait if buffer full

                SEI                 ;Disable interrupts
                STX TXTop           ;Save new offset
                DEX
                STA TXBuf, X        ;Put byte in buffer
                CLI                 ;Enable interrupts
                RTS

;---------------------------------------------------------------------
; GetString
; Terminates on CR
;
; Input     A = Address HI Byte
;           Y = Address LO Byte
;           X = Max Byte Count
;---------------------------------------------------------------------
GetString       STA PointerHI
                STY Pointer
                DEX
                STX MReg0
                LDY #0              ;Clear offset

GetString1      JSR GetChar         ;Get a char
                BEQ GetString1      ;Retry if no data ready

                CMP #CR             ;Compare to Carrage Return
                BEQ GetStringX      ;Exit if match

                STA (Pointer), Y    ;Save byte in buffer
                INY                 ;Increment offset
                CPY MReg0           ;Test for end of buffer
                BNE GetString1      ;Continue if buffer not full

GetStringX      LDA #0              ;Add terminating zero
                STA (Pointer), Y
                RTS

;---------------------------------------------------------------------
; PutString
; Terminates on NULL char
;
; Input     A = Address HI Byte
;           Y = Address LO Byte
;---------------------------------------------------------------------
PutString       STA PointerHI       ;Save address in Pointer
                STY Pointer
                LDY #0              ;Clear offset

PutString1      LDA (Pointer),Y     ;Get a byte
                BEQ PutStringX      ;Exit if end of string marker

                JSR PutChar         ;Output the byte
                INY                 ;Increment offset
                JMP PutString1      ;Loop for next byte

PutStringX      RTS

;---------------------------------------------------------------------
; Command Loop
;
; :...      Download HEX File
; D Adrs    Dump Memory
; M Adrs    Modify Memory
; J Adrs    Jump to Address
;---------------------------------------------------------------------
CmdLoop         LDA #<PromptMsg     ;Display prompt
                LDY #>PromptMsg
                JSR PutString

                LDA #<CmndBuf
                LDY #>CmndBuf
                LDX #128
                JSR GetString       ;Get user input

                LDA CmndBuf         ;Decode first char of command
                ORA #$20            ;Convert to lower case

                CMP #':'            ;Download HEX File
                BNE CmdLoop1
                JSR DownLoad
                JMP CmdLoop

CmdLoop1        CMP #'d'            ;Dump Memory
                BNE CmdLoop2
                JSR DumpMemory
                JMP CmdLoop

CmdLoop2        CMP #'j'            ;Jump to RAM code
                BNE CmdLoop9
                JSR $0500
                JMP CmdLoop

CmdLoop9        CMP #'?'
                BNE CmdLoopX
                LDA #<MenuMsg
                LDY #>MenuMsg
                JSR PutString
                JMP CmdLoop

CmdLoopX        JMP CmdLoop

PromptMsg       DB  ">",0
MenuMsg         DB  "D = Dump Memory", CR
                DB  "J = Jump To Code In RAM", CR, 0

;---------------------------------------------------------------------
; DumpMemory
;
; MReg6   = Line Count
; MReg7   = Byte Count
; Pointer = Source Address
;---------------------------------------------------------------------
DumpMemory      LDA #8              ;Init line count
                STA MReg6
                LDA #0
                STA Pointer         ;Init source address
                LDA #$05
                STA PointerHI

DumpMemory1     LDA #16             ;Init byte count
                STA MReg7
                LDA PointerHI
                JSR PrintHex
                LDA Pointer
                JSR PrintHex

DumpMemory2     LDA #' '
                JSR PutChar
                LDY #0
                LDA (Pointer),Y
                JSR PrintHex
                INC Pointer
                BNE DumpMemory3
                INC PointerHI

DumpMemory3     DEC MReg7
                BNE DumpMemory2
                LDA #CR
                JSR PutChar
                DEC MReg6
                BNE DumpMemory1
                RTS

;---------------------------------------------------------------------
; DownLoad
;
; MReg2 = CheckSum
; MReg3 = Byte Count
; MReg4 = Address LO
; MReg5 = Address HI
;---------------------------------------------------------------------
DownLoad        LDX #>CmndBuf       ;Setup pointer to ASCII input
                INX
                STX Pointer
                LDA #<CmndBuf
                STA PointerHI

                LDA #0
                STA MReg2           ;Clear checksum

                JSR Scan2Hex
                STA MReg3           ;Byte Count
                CLC                 ;Add byte to checksum
                ADC MReg2
                STA MReg2

                JSR Scan2Hex
                STA MReg5           ;Address HI Byte
                CLC                 ;Add byte to checksum
                ADC MReg2
                STA MReg2

                JSR Scan2Hex
                STA MReg4           ;Address LO Byte
                CLC                 ;Add byte to checksum
                ADC MReg2
                STA MReg2

                JSR Scan2Hex
                CMP #0              ;Hex Line Type
                BNE DownLoadX       ;Abort if not zero

DownLoad1       JSR Scan2Hex
                LDY #0
                STA (MReg4),Y       ;Save data byte
                CLC                 ;Add byte to checksum
                ADC MReg2
                STA MReg2

                INC MReg4           ;Increment destination pointer
                BNE DownLoad2
                INC MReg5

DownLoad2       DEC MReg3           ;Decrement byte count
                BNE DownLoad1       ;Loop until count zero

                JSR Scan2Hex
                CLC
                ADC MReg2           ;Add to checksum
                BEQ DownLoadX       ;Done if result is zero

                LDA #'x'
                JSR PutChar

DownLoadX       RTS

;---------------------------------------------------------------------
; Scan2Hex
;
; Input     Pointer = Address of ASCII string
;
; Output    A = Value Byte
;           Pointer is incremented
;
; Temps     MReg0
;---------------------------------------------------------------------
Scan2Hex        JSR HexToInt
                ASL A
                ASL A
                ASL A
                ASL A
                STA MReg0
                JSR HexToInt
                ORA MReg0
                RTS

;---------------------------------------------------------------------
; HexToInt
;
; Input     Pointer = Address of ASCII string
;
; Output    A = Value, 0-15
;           C = Success
;           Pointer is incremented
;---------------------------------------------------------------------
HexToInt        LDY #0
                LDA (Pointer),Y
                INC Pointer
                BNE HexToInt1
                INC PointerHI

HexToInt1       CMP #48
                BCC HexToIntX

                CMP #58
                BCS HexToInt2
                AND #15
                SEC
                RTS

HexToInt2       ORA #$20
                CMP #97
                BCC HexToIntX

                CMP #103
                BCS HexToIntX
                SBC #86
                SEC
                RTS

HexToIntX       CLC
                RTS

;---------------------------------------------------------------------
; PrintHex
;
; Input     A = Byte to print
;---------------------------------------------------------------------
PrintHex        PHA
                LSR A
                LSR A
                LSR A
                LSR A
                TAX
                LDA HexASCIITable, X
                JSR PutChar
                PLA
                AND #15
                TAX
                LDA HexASCIITable, X
                JSR PutChar
                RTS

HexASCIITable   DB  "0123456789ABCDEF"

;---------------------------------------------------------------------
VIA_Int         JSR VIA_Int_Jmp
VIA_Int_Exit    RTI
VIA_Int_Jmp     JMP ($00F0) ;(VIA_IntVec)

;---------------------------------------------------------------------

                ORG $FF00
                JMP GetChar         ;$FF00
                JMP PutChar         ;$FF03
                JMP GetString       ;$FF06
                JMP PutString       ;$FF09

;---------------------------------------------------------------------
                ORG $FFFA
                DW  VIA_Int
                DW  MonStart
                DW  UART_Int
