    .CR 8080
    .EB OFF
    .TF BOOTLOAD.HEX,INT
    .LI off
    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                              ;
;                             8080 CPU Bootloader                              ;
;                                                                              ;
; This is a simple bootloader that will write data to memory that is sent      ;
; over the UART. It occupies a little over half of a 768 byte ROM.             ;
;                                                                              ;
; It has three commands:                                                       ;
;   * Waaaadd - 'Write' - where aaaa is the address and dd is the data. Note   ;
;               that all numerals are in CAPITAL hexadecimal, as are all the   ;
;               commands. This command will echo back the address and the      ;
;               data that was written, however, if the address is in the ROM's ;
;               address space this will be data FROM THE ROM, so it will not   ;
;               match what was written to the RAM. Note that the Python        ;
;               programmer does not check the data written below 0x2FF (the    ;
;               high address of the ROM) for this very reason.                 ;
;   * C       - 'Clear' - clears the RAM.                                      ;
;   * D       - 'Done' - this will write a one to the switch register and      ;
;                        activate the RAM, thus resetting the CPU and          ;
;                        executing whatever is in RAM.                         ;
;                                                                              ;
; The bootloader uses the top end of the RAM as a small stack space. It would  ;
; be possible to get rid of the need for a stack altogether by 'unrolling' the ;
; code but I was too lazy, so I just made sure that the programs I was loading ;
; didn't have anything near the end of the RAM.                                ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                              ;
; Copyright (c) 2008 Mike Roddewig.                                            ;
; All rights reserved.                                                         ;
;                                                                              ;
; Redistribution and use in source and binary forms, with or without           ;
; modification, are permitted provided that the following conditions are met:  ;
;   * Redistributions of source code must retain the above copyright notice,   ;
;     this list of conditions and the following disclaimer.                    ;
;   * Redistributions in binary form must reproduce the above copyright        ;
;     notice, this list of conditions and the following disclaimer in the      ;
;     documentation and/or other materials provided with the distribution.     ;
;                                                                              ;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS          ;
; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED    ;
; TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR   ;
; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR             ;
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,        ;
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,          ;
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;  ;
; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,     ;
; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR      ;
; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF       ;
; ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                   ;
;                                                                              ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    .OR $0000
;    
; UART equates    
;
TTS:        .EQ     $20
TTI:        .EQ     $21
TTO:        .EQ     $21
TTYDA:      .EQ     $01
TTYTR:      .EQ     $80

SWITCH:     .EQ     $00     ; Address of the memory switch peripheral.

MEM_HIGH:   .EQ $FFFF       ; The highest memory address.
    
START:      MVI A,$00
            MVI B,$00
            MVI C,$00
            MVI D,$00
            MVI E,$00
            MVI H,$00
            MVI L,$00
            LXI SP,MEM_HIGH-20  ; Setting up the stack space.
            LXI H,WMES
            CALL PRINT
            JMP CLEAR       ; Clear the memory at start-up
            
PROGRAM:    CALL IN8
            MOV B,A
            MVI A,'D'
            CMP B
            JZ DONE
            MVI A,'W'
            CMP B
            JZ WRITE
            MVI A,'C'
            CMP B
            JZ CLEAR
            JMP PROGRAM
            
            ; This routine will clear the ram (including the stack!!!)
CLEAR:      CALL OUT8
            LXI H,$0000
            MVI A,$FF
CLEAR_LOOP: MVI M,$00
            CMP H
            JZ CLEAR_LB
CLEAR_INC:  INX H
            JMP CLEAR_LOOP
CLEAR_LB:   CMP L
            JNZ CLEAR_INC
            CALL CRLF
            JMP PROGRAM
            
            ; This subroutine will terminate the bootloader.
DONE:       CALL OUT8
            CALL CRLF
            MVI A,$01
            OUT SWITCH  ; This will reset the processor...
NOP_LOOP:   NOP
            JMP NOP_LOOP
            
WRITE:      CALL OUT8
            CALL GET_ADDR
            CALL IN8
            CALL ATOH
            RLC
            RLC
            RLC
            RLC
            MOV B,A
            CALL IN8
            CALL ATOH
            ADD B
            MOV M,A     ; Store data to memory.
            CALL W_ADDR ; Echo the address back
            MOV A,M     ; Load the data back.
            CALL WRITE_A
            CALL CRLF
            JMP PROGRAM
            
            ; This subroutine will write the byte in A to the UART.
WRITE_A:    MOV C,A     ; Save the byte
            ANI $F0
            RRC
            RRC
            RRC
            RRC
            CALL HTOA
            MOV B,A
            CALL OUT8
            MOV A,C
            CALL HTOA
            MOV B,A
            CALL OUT8
            RET
            
            ; This subroutine will convert an ASCII hex number in A
            ; to a number.
ATOH:       CPI '9'+1
            JC ATOH_NUM
ATOH_CHAR:  SUI $37
            JMP ATOH_DONE
ATOH_NUM:   SUI $30
ATOH_DONE:  ANI $0F
            RET
            
            ; This subroutine will convert a number in A 
            ; to a character.
HTOA:       ANI $0F
            CPI 10
            JC HTOA_NUM
            ADI $37
            JMP HTOA_DONE
HTOA_NUM:   ADI $30
HTOA_DONE:  RET

            ; This subroutine will write the address in HL to the
            ; UART.
W_ADDR:     MVI D,1
W_ADDR_L:   MOV A,D
            CPI 0
            JZ W_ADDR_D
            MOV A,H
            CALL WRITE_A
            DCR D
            JMP W_ADDR_L
W_ADDR_D:   MOV A,L
            CALL WRITE_A
            RET

            ; This subroutine reads an address from the UART into HL.
GET_ADDR:   MVI C,1 
GET_LOOP:   CALL IN8
            CALL ATOH
            RLC
            RLC
            RLC
            RLC
            MOV B,A
            CALL IN8
            CALL ATOH
            ADD B
            MOV B,A
            MOV A,C
            CPI 0
            JZ GET_DONE
            MOV H,B
            DCR C
            JMP GET_LOOP
GET_DONE:   MOV L,B
            RET

            ; This subroutine will print a null-terminated string 
            ; with the starting address loaded into HL.
PRINT:      MOV B,M     ; fetch character
            MVI A,$00   ; null
            CMP B       ; character = null?
            RZ      
            CALL OUT8
            INX H
            JMP PRINT
            
            ; This subroutine sends the character in B to the
            ; UART.
OUT8:       IN TTS
            CMA
            ANI TTYTR
            JZ OUT8
            MOV A,B
            OUT TTO
            RET
            
            ; This subroutine fetches a character from the UART
            ; and returns it in A.
IN8:        IN TTS
            CMA
            ANI TTYDA
            JZ IN8
            IN TTI
            RET
            
CRLF:       MVI B,13    ; cr
            CALL OUT8
LF:         MVI B,10    ; lf
            CALL OUT8
            RET
            
WMES:       .DB '8080 Bootloader',13,10
            .DB 'Mike Roddewig 2008',00