$MOD552

; MMU-controlling pins, after RESET MODE0=1 and MODE1=1
MODE0   equ     P4.5
MODE1   equ     P4.4

; Memory pointer 
POINTER equ     R0

; Record type
RECORD_TYPE equ R2

; Counter for different routines
COUNTER equ     R3

; Checksum calculated for received data
CHKSUM equ      R4

; Flag used by memory test
MEMTEST equ     R5

; Number of bytes in received record
NUM_OF_BYTES data 40h

; Received checksum
CHKSUM2 data 41H

; Temporary variable
TEMP data 42h

; Higher and lower bytes of target address of received record
ADRL data 43H
ADRH data 44H

; Stack location
STACKADR equ 08h

; Data buffer (here are placed received by serial interface data)
BUFOR data 46h

  org   0                       ;After memory is switched over
  ajmp  INIT                    ;these vectors are available above 8000h
  org   3                       ;And provide access to:
  ajmp  WAIT_FOR_NEW_RECORD     ; Downloading procedure
  org   6
  setb  MODE0                   ; "Warm" restart
  ljmp  0H

  org   100h
;Prepare hardware for work
INIT:
; Clear registers
  clr   A
  mov   PSW,A
  mov   TCON,A
  mov   IEN0,A
  mov   IEN1,A
  mov   S0CON,A
  mov   S1CON,A
; Set stack pointer
  mov   SP,#STACKADR


; Set SFR registers
  mov   TMOD,#20H ; Timer 1: 8-bit auto-reload timer
  mov   TH1,#0FAH ; Set transmission rate to 10417 bit/s 
  mov   TL1,TH1   ; (this value is the best suited to IBM RS232, 
  orl   PCON,#80H ; from all high speeds available in 8051 for 12 MHz XTAL)
  setb  TR1       ; Switch on timer 1
  mov   S0CON,#40H ; 8-bit UART
  setb  REN
  clr   RI
  clr   TI

;The hardware is ready - send the prompt character and receive program
  call  SEND_PROMPT
  sjmp   WAIT_FOR_NEW_RECORD
;Following procedures send to the host computer error messages
;Error "Transmitted data couldn't be downloaded to the RAM"
MEMORY_WRITE_ERROR:
  jnb   TI,$
  clr   TI
  mov   S0BUF,#'#'
  sjmp  WAIT_FOR_NEW_RECORD
;Error "Transmission or protocol error"
RECEIVE_ERROR:
  jnb   TI,$
  clr   TI
  mov   S0BUF,#'?'
;This procedure receives new hex-record
WAIT_FOR_NEW_RECORD:
  jnb   RI,$
  mov   A,S0BUF
  clr   RI
  cjne  A,#':',RECEIVE_ERROR
NEW_HEX_RECORD:
  ;Because sometimes, after an error, processor jumps here from inside
  ; of other procedures, at first we have to clean up the stack
  mov   SP,#STACKADR
  mov   CHKSUM,#0
  acall GETBYTE_CHK
  mov   NUM_OF_BYTES,A           ;Number of bytes in record received
  acall GETBYTE_CHK
  mov   ADRH,A
  acall GETBYTE_CHK
  mov   ADRL,A                   ;Target address of record data received
  acall GETBYTE_CHK
  mov   RECORD_TYPE,A
  mov   COUNTER,NUM_OF_BYTES
  mov   POINTER,#BUFOR
NEXT_BYTE:   
  cjne  COUNTER,#0,NEXT_BYTE_TO_BUFFER
  ;If jump is not performed - the last byte is received,
  ;so we have to receive and check the checksum.
  acall GETBYTE_CHK
  mov   CHKSUM2,A
  cjne  CHKSUM,#0,RECEIVE_ERROR
  cjne  RECORD_TYPE,#0,PROBABLY_END_RECORD
  acall TRANSFER_BUFFER_DATA
  cjne  MEMTEST,#0ffh,BUFOR_OK ;Check if data are correctly written to RAM
  ajmp  MEMORY_WRITE_ERROR
BUFOR_OK:
  acall SEND_ACK
  sjmp  WAIT_FOR_NEW_RECORD ;Prepare for reception of next record

NEXT_BYTE_TO_BUFFER:
  acall GETBYTE_CHK
  mov   @POINTER,A
  inc   POINTER
  dec   COUNTER
  sjmp  NEXT_BYTE
PROBABLY_END_RECORD:
  cjne  RECORD_TYPE,#1,RECEIVE_ERROR
  acall SEND_ACK
  jnb   TI,$

;After acknowledge has been sent, jump to the program start procedure
;found in the higher half of code memory (because the lower part of
;code memory is to be switched over).
  ljmp  8000h+PROGRAM_RUN 

; This procedure transfers the received record to the target address in RAM
;
TRANSFER_BUFFER_DATA:
  mov   A,ADRH
  orl   A,#80h ;Add 80h, because RAM found in the higher half of data space
	      ;will be seen in the lower half of code space, after memory 
	      ;is switched over
  mov   DPH,A
  mov   DPL,ADRL
  mov   POINTER,#BUFOR
  mov   COUNTER,NUM_OF_BYTES
TRANSFER_LOOP:
  cjne  COUNTER,#0,TRANSFER_NEXT
  sjmp  CHECK_BUFFER
TRANSFER_NEXT:
  mov   A,@POINTER
  movx  @DPTR,A
  inc   POINTER
  inc   DPTR
  dec   COUNTER
  sjmp  TRANSFER_LOOP
CHECK_BUFFER:
  mov   MEMTEST,#0ffh
  mov   A,ADRH
  orl   A,#80h
  mov   DPH,A
  mov   DPL,ADRL
  mov   POINTER,#BUFOR
  mov   COUNTER,NUM_OF_BYTES
CHECK_LOOP:
  cjne  COUNTER,#0,CHECK_NEXT
  mov   MEMTEST,#0
CHECK_RET:
  ret
CHECK_NEXT:
  movx  A,@DPTR
  mov   TEMP,@POINTER
  cjne  A,TEMP,CHECK_RET
  inc   POINTER
  inc   DPTR
  dec   COUNTER
  sjmp  CHECK_LOOP

;
; This procedure starts execution of loaded program
;
PROGRAM_RUN:
  clr   MODE0
  mov   r0,#0ffh
START_DELAY:
  mov   r1,#0ffh
  djnz  r1,$
  djnz  r0,START_DELAY
  push  ADRL
  push  ADRH
  ret

; This procedure receives hex-byte and modifies checksum
; In the case of error this procedure does not return but
; jumps to the RECEIVE_ERROR
GETBYTE_CHK:
  acall GETBYTE
  push  ACC
  add   A,CHKSUM
  mov   CHKSUM,A
  pop   ACC
  ret

; This procedure sends positive acknowledge to the host
SEND_ACK:
  jnb   TI,$
  clr   TI
  mov   S0BUF,#'!'  ; This char is acknowlegment's header
  jnb   TI,$
  clr   TI
  mov   S0BUF,ADRH  ; Send address of downloaded data
  jnb   TI,$
  clr   TI
  mov   S0BUF,ADRL
  jnb   TI,$
  clr   TI
  mov   S0BUF,CHKSUM2 ; Send received chksum
  ret  

;This procedure receives hex-byte
GETBYTE:
  acall GETTETR
  cjne  A,#0ffh,GB_NEXT1
  ajmp  RECEIVE_ERROR
GB_NEXT1:
  swap  A
  mov   TEMP,A
  acall GETTETR
  cjne  A,#0ffh,GB_NEXT2
  ajmp  RECEIVE_ERROR
GB_NEXT2:
  orl   A,TEMP
  ret

;This procedure receives one nibble of byte
; If an ':' character is received, this procedure does not return,
; but jumps directly to the NEW_HEX_RECORD label

GETTETR:
  jnb   RI,$
  mov   A,S0BUF
  clr   RI
  cjne  A,#':',GT_NEXT
  ajmp  NEW_HEX_RECORD
GT_NEXT:
  cjne  A,#'0',$+3
  jc    GT_ERROR
  cjne  A,#'9'+1,$+3
  jnc   GT_IS_A_TO_F?
  subb  A,#'0'-1 ;(Carry is already set)
  ret
GT_IS_A_TO_F?:
  cjne  A,#'A',$+3
  jc    GT_ERROR
  cjne  A,#'F'+1,$+3
  jnc   GT_ERROR
  subb  A,#'A'-11 ;(Carry is already set)
  ret
GT_ERROR:
  mov   A,#0ffh
  ret 

; This procedure sends prompt to the host computer
SEND_PROMPT:
  mov   DPTR,#PROMPT
  clr   A
  movc  A,@A+DPTR
  mov   S0BUF,A      ; Send always at least one char
SP_LOOP:
  inc   DPTR
  clr   A
  movc  A,@A+DPTR
  jz    SP_RET
  jnb   TI,$
  clr   TI
  mov   S0BUF,A
  sjmp  SP_LOOP
SP_RET:
  ret

; This is the prompt message, sent to the host after EV51WZ1 is switched on
PROMPT: DB 'EV552WZ1 (c) W.M.Zabolotny 09.1999'
   DB 10,13
   DB 'This loader is available under the "Artistic Licence"'
   DB 10,13
   DB 'see http://language.perl.com/misc/Artistic.html for details'
   DB 10,13
   DB 'Ready'
   DB 10,13,0

END

 
