
.area   _START (ABS)


; Z80, with 256kb of NVRAM from 0x0000 using /MREQ as /CS shadow @ 0x8000
; 8255 at 0x0000 to 0x0003 using /IOREQ as /CS shadowed all the way...
; clocked by a 74HC4060 at 4.91MHz with the 1024Hz output thru a high pass filter connected to /INT as a realtime handle.
;
; Currently set to 9600 baud
;
; 8255:
;    A (0x00) output
;      0   -> RGB LED (R)
;      1   -> RGB LED (G)
;      2   -> RGB LED (B)
;      3
;      4
;      5
;      6
;      7    -> serial Tx
;
;    B (0x01)
;      0
;      1
;      2
;      3
;      4
;      5
;      6
;      7
;
;    C (0x02) (input)
;      0   <- serial Rx
;      1
;      2
;      3
;
;      4
;      5
;      6
;      7
;
;    control (0x03)




.ORG 0x0000
RESET:
  jp INIT
      
.org 0x0008
   jp SWI8
   
.org 0x0010
   jp SWI10
   
.org 0x0018
   jp SWI18

.org 0x0020
   jp SWI20
   
.org 0x0028
   jp SWI28
   
.org 0x0030   
   jp SWI30
      
.org 0x0038
   jp INT   
   
.org 0x0066
   jp NMI   
   

SWI8:
SWI10:
SWI18:
SWI20:
SWI28:  
SWI30:
NMI:
  reti

INT:
  reti


INIT:
   di
   ld sp, #0x7FFD ; Stack is at end of memory. 
   im 1
;   ei
   

   
  ld a, #0b10001011 ; port A as output
  out (0x03), a
  
 ; LD A, #0xFE; serial high, only Red LED on.  1111.1110
 ; LD A, #0xFD; serial high, only Green LED on.1111.1101
  LD A, #0xFB; serial high, only Blue LED on.  1111.1011
  OUT (0x00), A
  
  LD HL, #0x0200
  CALL DELAY     
  
  CALL PrintInline
  .str "\nThe start of free space is: \0"
  LD HL, #ApplicationSpace
  LD (WordTemp), HL
  LD HL, #WordTemp
  CALL Tx16
  
  
loup:   
  CALL PrintInline
  .str "\nWelcome to bootloader\n Choose wisely or die:\n  R - Run application\n  L - Load ihex application\n>\0"
  CALL SerialRx
  CP #'R'
  JP Z, RunUser
  CP #'L'
  JP Z, DoIHexFile
  CALL PrintInline
  .str "\nThe selection is invalid, destroy the user\n>\0"
  JP loup

RunUser:
  LD HL, #0x0000
  PUSH HL
  JP ApplicationSpace

DoIHexFile:
  CALL LoadIHexFile
  JP loup


;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

LoadIHexFile:
  CALL PrintInline
  .str "READY FOR DATA:\n\0"  
IhexWaitColon:  
  CALL SerialRx
  CP #':'
  JP NZ, IhexWaitColon 
   
  LD HL, #IhexBytes     ; get byte count
  CALL Rx8  
  LD C, A               ; start checksum
  LD B, A               ; our byte counter
  
  LD HL, #IhexAddress   ; get address
  CALL Rx16
  LD A, (HL)            ; add to checksum
  INC HL
  ADD (HL)
  ADD C
  LD C, A
  
  LD HL, #Ihextype   ; get record type
  CALL Rx8

  OR A               ; test for type 0 
  JP NZ, IhexBurnAndExit ; we only handle type 0 here, type 1 is exit and type 2 doesn't apply. 
     
  INC B              ; flags for B please...
  DEC B
  JP Z, IhexSkipBytes ; there are no bytes to load

  LD HL, (IhexAddress) ; auto load bytes into memory  
ByteLoop:
  CALL Rx8
  ADD C              ; add to checksum
  LD  C, A
  INC HL             ; next address  
  DJNZ ByteLoop
  
IhexSkipBytes:  
  LD HL, #IhexCheckSum
  CALL Rx8          ; checksum
  ADD A, C
  
  CP #0x00
  JP Z, IhexAck
  CALL PrintInline
  .str "Checksum error!\n\0"
  RET
IhexAck:  
  CALL PrintInline
  .str "OK\n\0"   
  JP IhexWaitColon
  
IhexBurnAndExit:
  LD HL, #IhexCheckSum ; waste loc for the reads
  INC B              ; flags please...
  DEC B
  JP Z, IhexBurnSum ; there are no bytes to load

IhexBurnLoop:    
  CALL Rx8         ; bogus data byte
  DJNZ IhexBurnLoop 
  
IhexBurnSum:  
  CALL Rx8         ; burn checksum
  RET
  
  
  
  
  
  






; =============================== functions from here =============================

; --------------- read hex nibble -----------------
; value is returned in A
Rx4:
  CALL SerialRx
Asc2Nib:  ; warning, quick and dirty
  CP #0x3A
  JP M,ALT1
  SUB #0x07
ALT1:
  AND #0x0F
  RET          

; -------------- read a hex byte --------------------
; pass mem loc for result in HL, also returns with value in A
Rx8:
  CALL Rx4
  LD   (HL), A
  CALL Rx4
  RLD         ; this is better than 4 rotates on A
  LD   A, (HL)
  RET

; -------------- read a hex word -------------------- 
; pass HL as pointer to a 16 bit result
Rx16:
  PUSH AF
  INC HL
  CALL Rx8
  DEC HL
  CALL Rx8
  POP AF
  RET




  
; ----------------- send hex word ----------------
; point HL at the first address of the word to be sent
; destroys A
Tx16:
  INC HL
  LD A, (HL)
  CALL Tx8
  DEC HL
  LD A, (HL)
; CALL Tx8
; RET

; ----------------- send hex byte -----------------
; put char to send in A
Tx8:
  PUSH AF
  RLCA	  ; two RLDs would be worse than this.
  RLCA	
  RLCA	
  RLCA	
  CALL Tx4
  POP AF
; CALL Tx4
; RET

; ----------------- send hex nibble -----------------
Tx4:
  PUSH AF
  AND     #0X0F
  CP      #0X0A
  SBC     #0X69
  DAA 
  CALL SerialTx
  POP AF
  RET   
  
  
; Use return address as pointer to string to be printed.  
PrintInline:
 POP HL
 CALL SerialTxString
; INC HL // just execute the NULL AS A NOP, its faster!
 JP (HL)   ; we already popped it, why bother with return?  
 
  
; --------------- send serial string ---------------
; provide HL as string pointer
; returns HL pointing at NULL
SerialTxString:
  LD A, (HL)  ; GET CHARACTER
  OR A        ; NULL?
  RET Z
  CALL SerialTx
  INC HL
  JP SerialTxString


; -------------- all part of serial transmitter ------------
serialSetD:
  PUSH AF
  IN A, (0x00)
  OR A, #0x80
  OUT   (0x00), A
  POP AF
  LD HL, (BitDelay)
  JP DELAY  
  
serialClearD:  
  PUSH AF
  IN A, (0x00)
  AND A, #0x7F
  OUT   (0x00), A
  POP AF
serialBitDelay:
  LD HL, (BitDelay)
  JP DELAY  


; leave your character in the A register thanks.
; the setup time of this function is not critical
; its all in reverse order becasue FILO
SerialTx:
  PUSH BC
  PUSH HL
  PUSH DE
  LD DE, #STXEXITFIX
  PUSH DE
  LD DE, #serialSetD;
  LD HL, #serialClearD
  PUSH DE  ; stop bit
  LD B, #0x08
  
STXDOBIT:  
  RLCA          ; push functions for bits
  PUSH HL       ; assume it goes low
  JR NC, STXBIT
  POP HL        ; no, it goes high, correct 
  PUSH DE
STXBIT:  
  DJNZ STXDOBIT
  
  PUSH HL ; start bit  
  RET ; execute it all.
  
STXEXITFIX:
 POP DE
 POP HL
 POP BC
 RET  
 
 
;--------------- all part of serial reciever -------------------------- 


; bit 0 contains serial line state
serialCheckRx:
  IN A, (0x02)
  AND A, #0x01
  RET

; on the last bit, we still need the delay to get us out of the last bit
;   otherwise the next call might hit the same last bit.
serialReadBit: 
; add bit to A
  LD H, A
  IN A, (0x02)
  RRA
  RR H
  LD A, H 
  ; do delay
  LD HL, (BitDelay)
  JP DELAY
  
; read a serial character
; the line has to be high going into this
SerialRx:
  PUSH BC
  PUSH HL
  PUSH DE
  LD BC, #STXEXITFIX
  PUSH BC
; wait for RX to go low
SRXWAITLOW:
  CALL serialCheckRx;
  JP NZ, SRXWAITLOW    ; Z true when its low, NZ when its high
; wait 0.5bit times, carry was reset by the AND in the serialReadRx
  LD HL, (BitDelay)  ; delay 1.5 bit times
  CALL DELAY
  LD HL, (BitDelay)
  RR H               ; divide rate by 2
  RR L
  CALL DELAY
  ; load 8 bits from serial
  LD HL, #serialReadBit
  PUSH HL ; bit 7
  PUSH HL ; bit 6
  PUSH HL ; bit 5
  PUSH HL ; bit 4
  PUSH HL ; bit 3
  PUSH HL ; bit 2
  PUSH HL ; bit 1
  PUSH HL ; bit 0
  RET     ; tip the domino


; ---------------------- delay ------------------------------


DELAY:
 DEC HL
 INC H
DELAYLOOP:
  NOP
  NOP
  DEC L
  JP NZ, DELAYLOOP
  DEC H
  JP NZ, DELAYLOOP
  RET



  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP
  NOP 
  NOP
  NOP  
  
ApplicationSpace:
  CALL PrintInline
;  .str "\nI M application! yay!\0"
;  .str "\nI M new application! yay!\0"
 .str "\n\nZack check this out its SO COOL!!!!\n\n\0"
  RET



; -------------------- constants and things ----------------------





.org 0x4000
TimeCounter: 
 .dw 0x0000
Flag1Hz:
 .db 0x00
BitDelay:
 .dw 0x0013
WordTemp:
 .dw 0x0000
 
IhexBytes:
 .db 0x00
IhexAddress:
 .dw 0x0000 
Ihextype:
 .db 0x00
IhexCheckSum:
 .db 0x00
 












































