;   *************************************************
;   *                                               *
;   *  MINMON - The Minimal 8051 Monitor Program    *
;   *                                               *
;   *  Portions of this program are courtesy of     *
;   *  Rigel Corporation, of Gainesville, Florida   *
;   *                                               *
;   *  Modified for 6.115                           *   
;   *  Massachusetts Institute of Technology        *
;   *  January, 2001  Steven B. Leeb                *
;   *                                               *
;   *************************************************
stack  equ  2fh           ; bottom of stack
                          ; - stack starts at 30h -
errorf equ   0            ; bit 0 is error status
;=================================================================
; 8032 hardware vectors
;=================================================================
   org 00h                ; power up and reset vector
   ljmp start
   org 03h                ; interrupt 0 vector
   ljmp start
   org 0bh                ; timer 0 interrupt vector
   ljmp start
   org 13h                ; interrupt 1 vector
   ljmp start
   org 1bh                ; timer 1 interrupt vector
   ljmp start
   org 23h                ; serial port interrupt vector
   ljmp start
   org 2bh                ; 8052 extra interrupt vector
   ljmp start
;=================================================================
; begin main program
;=================================================================
   org     100h
start:
   clr     ea             ; disable interrupts
   lcall   init           
; initialize hardware
   lcall   print          ; print welcome message
   db 0ah, 0dh, "Welcome to 6.115!", 0ah, 0dh,"MINMON> ", 0h
monloop:
   mov     sp,#stack      ; reinitialize stack pointer
   clr     ea             ; disable all interrupts
   clr     errorf         ; clear the error flag
   lcall   print          ; print prompt
   db 0dh, 0ah,"*", 0h
   clr     ri             ; flush the serial input buffer
   lcall   getcmd         ; read the single-letter command
   mov     r2, a          ; put the command number in R2
   ljmp    nway           ; branch to a monitor routine
endloop:                  ; come here after command has finished
   sjmp monloop           ; loop forever in monitor loop
;=================================================================
; subroutine init
; this routine initializes the hardware
; set up serial port with a 11.0592 MHz crystal,
; use timer 1 for 9600 baud serial communications
;=================================================================
init:
   mov   tmod, #20h       ; set timer 1 for auto reload - mode 2
   mov   tcon, #41h       ; run counter 1 and set edge trig ints
   mov   th1,  #0fdh      ; set 9600 baud with xtal=11.059mhz
   mov   scon, #50h       ; set serial control reg for 8 bit data
                          ; and mode 1
   ret
;=================================================================
; monitor jump table
;=================================================================
jumtab:
   dw badcmd              ; command '@' 00
   dw badcmd              ; command 'a' 01
   dw badcmd              ; command 'b' 02
   dw badcmd              ; command 'c' 03
   dw downld              ; command 'd' 04 used
   dw badcmd              ; command 'e' 05
   dw badcmd              ; command 'f' 06
   dw goaddr              ; command 'g' 07 used
   dw badcmd              ; command 'h' 08
   dw badcmd              ; command 'i' 09
   dw badcmd              ; command 'j' 0a
   dw badcmd              ; command 'k' 0b
   dw badcmd              ; command 'l' 0c
   dw badcmd              ; command 'm' 0d
   dw badcmd              ; command 'n' 0e
   dw badcmd              ; command 'o' 0f
   dw badcmd              ; command 'p' 10
   dw badcmd              ; command 'q' 11
   dw badcmd              ; command 'r' 12
   dw badcmd              ; command 's' 13
   dw badcmd              ; command 't' 14
   dw badcmd              ; command 'u' 15
   dw badcmd              ; command 'v' 16
   dw badcmd              ; command 'w' 17
   dw badcmd              ; command 'x' 18
   dw badcmd              ; command 'y' 19
   dw badcmd              ; command 'z' 1a

;*****************************************************************
; monitor command routines
;*****************************************************************
;===============================================================
; command goaddr  'g'
; this routine branches to the 4 hex digit address which follows 
;===============================================================
goaddr:
   lcall getbyt           ; get address high byte
   mov   r7, a            ; save in R7
   lcall prthex
   lcall getbyt           ; get address low byte
   push  acc              ; push lsb of jump address
   lcall prthex
   lcall crlf
   mov   a, r7            ; recall address high byte
   push  acc              ; push msb of jump address
   ret                    ; do jump by doing a ret
;===============================================================
; command downld  'd'
; this command reads in an Intel hex file from the serial port
; and stores it in external memory.
;===============================================================
downld:
   lcall crlf
   mov   a, #'>'          ; acknowledge by a '>'
   lcall sndchr
dl:
   lcall getchr           ; read in ':'
   cjne  a,  #':', dl
   lcall getbytx          ; get hex length byte
   jz    enddl            ; if length=0 then return
   mov   r0, a            ; save length in r0
   lcall getbytx          ; get msb of address
   setb  acc.7            ; make sure it is in RAM
   mov   dph, a           ; save in dph
   lcall getbytx          ; get lsb of address
   mov   dpl, a           ; save in dpl
   lcall getbytx          ; read in special purpose byte (ignore)
dloop:
   lcall getbytx          ; read in data byte
   movx  @dptr, a         ; save in ext mem
   inc   dptr             ; bump mem pointer
   djnz  r0, dloop        ; repeat for all data bytes in record
   lcall getbytx          ; read in checksum
   mov   a,  #'.'
   lcall sndchr           ; handshake '.'
   sjmp  dl               ; read in next record
enddl:
   lcall getbytx          ; read in remainder of the
   lcall getbytx          ; termination record
   lcall getbytx
   lcall getbytx
   mov   a,  #'.'
   lcall sndchr           ; handshake '.'
   ljmp  endloop          ; return
getbytx:
   lcall getbyt
   jb    errorf, gb_err
   ret
gb_err:
   ljmp badpar

;*****************************************************************
; monitor support routines
;*****************************************************************
badcmd:
   lcall print
   db 0dh, 0ah," bad command ", 0h
   ljmp endloop
badpar:
   lcall print
   db 0dh, 0ah," bad parameter ", 0h
   ljmp endloop
;===============================================================
; subroutine getbyt
; this routine reads in an 2 digit ascii hex number from the
; serial port. the result is returned in the acc.
;===============================================================
getbyt:
   lcall getchr           ; get msb ascii chr
   lcall ascbin           ; conv it to binary
   swap  a                ; move to most sig half of acc
   mov   b,  a            ; save in b
   lcall getchr           ; get lsb ascii chr
   lcall ascbin           ; conv it to binary
   orl   a,  b            ; combine two halves
   ret
;===============================================================
; subroutine getcmd
; this routine gets the command line.  currently only a
; single-letter command is read - all command line parameters
; must be parsed by the individual routines.
;
;===============================================================
getcmd:
   lcall getchr           ; get the single-letter command
   clr   acc.5            ; make upper case
   lcall sndchr           ; echo command
   clr   C                ; clear the carry flag
   subb  a, #'@'          ; convert to command number
   jnc   cmdok1           ; letter command must be above '@'
   lcall badpar
cmdok1:
   push  acc              ; save command number
   subb  a, #1Bh          ; command number must be 1Ah or less
   jc    cmdok2
   lcall badpar           ; no need to pop acc since badpar
                          ; initializes the system
cmdok2:
   pop   acc              ; recall command number
   ret
;===============================================================
; subroutine nway
; this routine branches (jumps) to the appropriate monitor
; routine. the routine number is in r2
;================================================================
nway:
   mov   dptr, #jumtab    ;point dptr at beginning of jump table
   mov   a, r2            ;load acc with monitor routine number
   rl    a                ;multiply by two.
   inc   a                ;load first vector onto stack
   movc  a, @a+dptr       ;         "          "
   push  acc              ;         "          "
   mov   a, r2            ;load acc with monitor routine number
   rl    a                ;multiply by two
   movc  a, @a+dptr       ;load second vector onto stack
   push  acc              ;         "          "
   ret                    ;jump to start of monitor routine


;*****************************************************************
; general purpose routines
;*****************************************************************
;===============================================================
; subroutine sndchr
; this routine takes the chr in the acc and sends it out the
; serial port.
;===============================================================
sndchr:
   clr  scon.1            ; clear the tx  buffer full flag.
   mov  sbuf,a            ; put chr in sbuf
txloop:
   jnb  scon.1, txloop    ; wait till chr is sent
   ret
;===============================================================
; subroutine getchr
; this routine reads in a chr from the serial port and saves it
; in the accumulator.
;===============================================================
getchr:
   jnb  ri, getchr        ; wait till character received
   mov  a,  sbuf          ; get character
   anl  a,  #7fh          ; mask off 8th bit
   clr  ri                ; clear serial status bit
   ret
;===============================================================
; subroutine print
; print takes the string immediately following the call and
; sends it out the serial port.  the string must be terminated
; with a null. this routine will ret to the instruction
; immediately following the string.
;===============================================================
print:
   pop   dph              ; put return address in dptr
   pop   dpl
prtstr:
   clr  a                 ; set offset = 0
   movc a,  @a+dptr       ; get chr from code memory
   cjne a,  #0h, mchrok   ; if termination chr, then return
   sjmp prtdone
mchrok:
   lcall sndchr           ; send character
   inc   dptr             ; point at next character
   sjmp  prtstr           ; loop till end of string
prtdone:
   mov   a,  #1h          ; point to instruction after string
   jmp   @a+dptr          ; return
;===============================================================
; subroutine crlf
; crlf sends a carriage return line feed out the serial port
;===============================================================
crlf:
   mov   a,  #0ah         ; print lf
   lcall sndchr
cret:
   mov   a,  #0dh         ; print cr
   lcall sndchr
   ret
;===============================================================
; subroutine prthex
; this routine takes the contents of the acc and prints it out
; as a 2 digit ascii hex number.
;===============================================================
prthex:
   push acc
   lcall binasc           ; convert acc to ascii
   lcall sndchr           ; print first ascii hex digit
   mov   a,  r2           ; get second ascii hex digit
   lcall sndchr           ; print it
   pop acc
   ret
;===============================================================
; subroutine binasc
; binasc takes the contents of the accumulator and converts it
; into two ascii hex numbers.  the result is returned in the
; accumulator and r2.
;===============================================================
binasc:
   mov   r2, a            ; save in r2
   anl   a,  #0fh         ; convert least sig digit.
   add   a,  #0f6h        ; adjust it
   jnc   noadj1           ; if a-f then readjust
   add   a,  #07h
noadj1:
   add   a,  #3ah         ; make ascii
   xch   a,  r2           ; put result in reg 2
   swap  a                ; convert most sig digit
   anl   a,  #0fh         ; look at least sig half of acc
   add   a,  #0f6h        ; adjust it
   jnc   noadj2           ; if a-f then re-adjust
   add   a,  #07h
noadj2:
   add   a,  #3ah         ; make ascii
   ret

;===============================================================
; subroutine ascbin
; this routine takes the ascii character passed to it in the
; acc and converts it to a 4 bit binary number which is returned
; in the acc.
;===============================================================
ascbin:
   clr   errorf
   add   a,  #0d0h        ; if chr < 30 then error
   jnc   notnum
   clr   c                ; check if chr is 0-9
   add   a,  #0f6h        ; adjust it
   jc    hextry           ; jmp if chr not 0-9
   add   a,  #0ah         ; if it is then adjust it
   ret
hextry:
   clr   acc.5            ; convert to upper
   clr   c                ; check if chr is a-f
   add   a,  #0f9h        ; adjust it
   jnc   notnum           ; if not a-f then error
   clr   c                ; see if char is 46 or less.
   add   a,  #0fah        ; adjust acc
   jc    notnum           ; if carry then not hex
   anl   a,  #0fh         ; clear unused bits
   ret
notnum:
   setb  errorf           ; if not a valid digit
   ljmp  endloop


;===============================================================
; mon_return is not a subroutine.  
; it simply jumps to address 0 which resets the system and 
; invokes the monitor program.
; A jump or a call to mon_return has the same effect since 
; the monitor initializes the stack.
;===============================================================
mon_return:
   ljmp  0
; end of MINMON
