;In-circuit programmer for the Atmel 89C2051 microcontroller

;A 87C51 based 89C2051 programmer.  Download code via serial
;port.  Command switches programmed 89C2051 into circuit
;via 4066 analog switches (80 ohm on resistance).  Reset is
;controlled by programmer, and crystal is local in programmer.

;this is an early BETA copy, version 0.003.  Use at your own risk!
;please report bugs to paul@ece.orst.edu.  Updates available at:
;http://www.ece.orst.edu/~paul/8051-goodies/goodies-index.html#atmel
;this version (0.003) was made publicly available on 28 Sept 95.
;The user interface is particularly unfriendly, so be wary.  This
;will improve in the next couple versions...

;ram memory is assumed to be available both between 0000-1FFF
;AND 2000-3FFF.  This will likely get cleaned up in a more
;elegant way in a later version.  Other strange hardware
;dependencies may exist... they will get cleaned up.  This
;BETA is the first version that seems to have all the major
;functions working correctly!

;uart txd pin of 89C2051 is logically or'd with the txd of the
;programmer while the chip is switched in-curcuit, so debugging
;information can be sent to the same console as the programming.
;rxd pin of 89C2051 not connected to programmer, however.

;This software is in the public domain.  I, Paul Stoffregen, give
;no warranty, expressed or implied for this software and/or
;documentation provided, including, without limitation, warranty
;of merchantability and fitness for a particular purpose. 



    ;timer reload calculation
    ; baud_const = 256 - (crystal / (12 * 16 * baud))
.equ    baud_const, 253         ;19200 baud w/ 11.0592 MHz                  

;memory locations for the 8255 chip
.equ    port_a, 0xE000
.equ    port_b, 0xE001
.equ    port_c, 0xE002
.equ    port_pgm, 0xE003

.equ    stack, 0x30

        .org    0
        ljmp    poweron         ;reset vector
        .org    3               ;ext int0 vector

        .org    11              ;timer0 vector
        .org    19              ;ext int1 vector

        .org    27              ;timer1 vector
        .org    35              ;uart vector
        .org    43              ;timer2 vector (8052)
        .org    48              ;finally we can begin


main:
        mov     r6, #0
        mov     r7, #0x20
        acall   newline
        acall   delay
        clr     tr0
        ;fill buffer with 0xFF
        mov     dptr, #0
inimain:
        mov     a, #0xFF
        movx    @dptr, a
        inc     dptr
        mov     a, dph
        cjne    a, #0x20, inimain

        ajmp    mainloop

;the main program loop:

mainloop:

        lcall   cin
        lcall   upper
        cjne    a, #'D', main2
        lcall   dnld
        sjmp    mainloop
main2:  cjne    a, #'?', main3
        mov     dptr, #help
        lcall   pstr
        sjmp    mainloop
main3:  cjne    a, #'P', main4
        lcall   prog
        sjmp    mainloop
main4:  cjne    a, #'I', main5
        lcall   in
        sjmp    mainloop
main5:  cjne    a, #'R', main6
        lcall   read
        sjmp    mainloop
main6:  cjne    a, #'H', main7
        lcall   hexdump
        sjmp    mainloop
main7:  cjne    a, #'E', main8
        lcall   erase
        sjmp    mainloop
main8:  cjne    a, #'S', main9
        lcall   readid
        sjmp    mainloop
main9:  cjne    a, #'T', main10
        lcall   testpvolt
        sjmp    mainloop
main10:
        ljmp    mainloop
      


help:   .db     13,10
        .db     "Atmel 89C2051 In-circuit programmer commands",13,10
        .db     "BETA VERSION 0.003 (28 Sept 95)",13,10
        .db     "Paul Stoffregen (paul@ece.orst.edu)",13,10,13,10
        .db     "  D - download Intel HEX file into buffer",13,10  
        .db     "  P - program the chip from the buffer",13,10
        .db     "  E - erase chip",13,10
        .db     "  I - in-circuit operation",13,10
        .db     "  R - read back into ram",13,10
        .db     "  S - read ID bytes",13,10
        .db     "  H - hexdump ram",13,10
        .db     "  ? - This help",13,10
        .db     13,10,0


out:    ;switch the 89C2051 out of the circuit
        setb    p1.0            ;disconnect from circuit
        clr     p3.5            ;stop the oscillator
        clr     p3.3            ;force xtal1 to low
        lcall   volt0           ;make sure reset is low
        lcall   volt5           ;force reset to high
        clr     p3.4            ;block its access to our serial line
        mov     dptr, #port_a
        mov     a, #00001111b   ;port a = 00001111
        movx    @dptr, a
        mov     dptr, #port_pgm
        mov     a, #10001011b   ;port a=out, b=in, c=in
        movx    @dptr, a
        mov     a, #00001111b
        mov     dptr, #port_a
        movx    @dptr, a        ;port a = 00001111
        ret


read:   ;read the 89C2051 back into memory 2800 to 2FFF

        lcall   out
        mov     dptr, #port_pgm
        mov     a, #10001011b   ;port a=out, b=in, c=in
        movx    @dptr, a
        mov     dptr, #port_a
        mov     a, #11100111b   ;read mode
        movx    @dptr, a
        mov     dptr, #0x2800
read1:
        push    dpl
        push    dph
        mov     dptr, #port_b
        movx    a, @dptr        ;read in data from 89C2051
        pop     dph
        pop     dpl
        movx    @dptr, a        ;write to ram
        inc     dptr
        setb    p3.3            ;advance addr counter
        nop
        nop
        nop
        nop
        clr     p3.3
        mov     a, dpl
        cjne    a, #0, read1
        mov     a, dph
        cjne    a, #0x30, read1
        ret



readid: ;try to read the product id bytes

        mov     dptr, #id_msg
        lcall   pstr
        lcall   out
        mov     dptr, #port_pgm
        mov     a, #10001011b   ;port a=out, b=in, c=in
        movx    @dptr, a
        mov     dptr, #port_a
        mov     a, #01000111b   ;read id mode
        movx    @dptr, a
        mov     dptr, #port_b
        movx    a, @dptr        ;read in data from 89C2051
        lcall   phex
        mov     a, #' '
        lcall   cout
        setb    p3.3            ;advance addr counter
        nop
        nop
        nop
        nop
        clr     p3.3
        mov     dptr, #port_b
        movx    a, @dptr        ;read in data from 89C2051
        lcall   phex
        mov     a, #' '
        lcall   cout
        setb    p3.3            ;advance addr counter
        nop
        nop
        nop
        nop
        clr     p3.3
        mov     dptr, #port_b
        movx    a, @dptr        ;read in data from 89C2051
        lcall   phex
        lcall   newline
        ret

id_msg: .db     13,10,"ID Bytes: ",0



in:     ;put the 89C2051 into the circuit and start it
        lcall   volt5           ;make sure reset is at 5 volts
        setb    p3.5            ;start the oscillator
        setb    p3.3            ;and allow it to drive the chip
        setb    p3.4            ;allow the 89C2051's TxD pin to
                                ;drive our serial output too
        lcall   dly_50us        ;wait a bit
        mov     dptr, #port_pgm
        mov     a, #10011011b   ;port a=in, b=in, c=in
        movx    @dptr, a
        clr     p1.0            ;connect it to the external circuit
        lcall   dly_50us        ;wait a little while
        lcall   volt0           ;begin operation!
        ;at this point, the Atmel 89C2051 should be in the
        ;circuit and running.  Now we wait for a keypress, and
        ;then we'll yank it out of the circuit again.
        lcall   cin
        lcall   out
        ret


prog:   ;program the 89C2051 using the data in memory
        ;from 2000 to 27FF

        lcall   out

        mov     dptr, #p_msg1
        lcall   pstr

        mov     dptr, #0x2000

prog1:
        movx    a, @dptr        ;get the value to program now
        mov     b, a            ;and keep it around in b
        cjne    a, #0xFF, prog1b
        ljmp    prog_skip       ;skip programming if 0xFF
prog1b:
        push    dph
        push    dpl
        mov     a, dpl
;        jnz     prog2
        mov     a, #13
        lcall   cout
        mov     dptr, #p_msg2
        lcall   pstr
        pop     dpl
        pop     dph
        push    dph
        push    dpl
        mov     a, dph
        lcall   phex
        mov     a, dpl
        lcall   phex

prog2:
        mov     dptr, #port_pgm
        mov     a, #10001001b   ;port a=out, b=out, c=in
        movx    @dptr, a
        mov     dptr, #port_b
        mov     a, b
        movx    @dptr, a        ;drive port 1 with data byte
        mov     dptr, #port_a
        mov     a, #11110111b
        movx    @dptr, a
        lcall   volt12          ;raise reset to 12 volts
        mov     dptr, #port_a
        mov     a, #11110011b
        movx    @dptr, a        ;begin prog pulse
        lcall   dly_50us
        mov     a, #11110111b
        movx    @dptr, a        ;end prog pulse
        ;the text description seems to suggest keeping reset at
        ;12 volts until it's done writing, but the detailed timing
        ;diagram says it's ok to lower reset back to 5 volts
        ;only 10us after the end of our prog pulse...
        lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
progwait:
        mov     dptr, #port_c
        movx    a, @dptr        ;read port #3
        anl     a, #00000001b   ;the lsb is our busy line
;should check here to make sure it's not stuck...  typical
;program time is 1.2ms, worst case is 2ms
        jz      progwait
        lcall   volt5           ;make sure we're not still at 12 volts
        ;get ready to read
        mov     dptr, #port_pgm
        mov     a, #10001011b   ;port a=out, b=in, c=in
        movx    @dptr, a
        mov     dptr, #port_a
        mov     a, #11100111b   ;read mode
        movx    @dptr, a
        nop
        nop
        nop
        mov     dptr, #port_b
        movx    a, @dptr        ;read data back from chip
        push    acc             ;push it into stack for now
        mov     dptr, #port_a
        mov     a, #11110111b   ;disable read mode
        movx    @dptr, a
        nop
        nop
        nop
        nop
        ;and now all we need to do is increment the addr counter
        setb    p3.3
        nop
        nop
        nop
        clr     p3.3
        ;now let's look at the data we verified
        pop     acc
        cjne    a, b, prog_err
prog_next:
        pop     dpl
        pop     dph
pskip2:
        inc     dptr
        mov     a, dpl
        cjne    a, #0, prog_j
        mov     a, dph
        cjne    a, #0x28, prog_j
        lcall   newline
        mov     dptr, #p_msg3
        lcall   pstr
        ret

prog_skip:
        ;don't forget to increment the addr counter when
        ;we skip a location because the buffer is 0xFF
        setb    p3.3
        nop
        nop
        nop
        clr     p3.3
        sjmp    pskip2


p_msg1: .db     "Programming Flash ROM:",13,10,0
p_msg2: .db     "Location: ",0
p_msg3: .db     "Finished Programming",13,10,0

prog_j: ;jump up to do the next location
        ljmp    prog1

prog_err:
        mov     r3, a           ;keep bogus value in r3
        mov     r4, b           ;keep correct value in r4
        mov     dptr, #pgm1
        lcall   pstr
        pop     dpl
        pop     dph
        push    dph
        push    dpl
        mov     a, dph
        lcall   phex
        mov     a, dpl
        lcall   phex
        mov     dptr, #pgm2
        lcall   pstr
        mov     a, r4
        lcall   phex
        mov     dptr, #pgm3
        lcall   pstr
        mov     a, r3
        lcall   phex
        lcall   newline
        ljmp    prog_next


pgm1:   .db     "  Program verify error at ",0
pgm2:   .db     " correct: ",0
pgm3:   .db     " actual: ",0



erase:  ;erase the 89C2051
        lcall   out
        mov     dptr, #er_msg1
        lcall   pstr
        mov     dptr, #port_a
        mov     a, #01001111b   ;erase code
        movx    @dptr, a
        nop
        nop
        lcall   volt12          ;go to 12 volts
        mov     dptr, #port_a
        mov     a, #01001011b   ;erase pulse
        movx    @dptr, a
er_dly: mov     r3, #40
er_dly2:lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
        lcall   dly_50us
        djnz    r3, er_dly2
        mov     dptr, #port_a
        mov     a, #01001111b   ;end erase pulse
        movx    @dptr, a
        nop
        nop
        lcall   volt5           ;back to 5 volts
        mov     dptr, #port_a
        mov     a, #11111111b
        movx    @dptr, a
        nop
        nop
        mov     dptr, #done
        lcall   pstr
        ret

er_msg1:.db     "Erasing Flash ROM...",0
done:   .db     "done",13,10,0

testpvolt:
        lcall   volt12
        mov     dptr, #progmsg1
        acall   pstr
        mov     a, #'1'
        acall   cout
        mov     a, #'2'
        acall   cout
        mov     dptr, #progmsg2
        acall   pstr
        acall   cin

        lcall   volt0
        mov     dptr, #progmsg1
        acall   pstr
        mov     a, #'0'
        acall   cout
        mov     dptr, #progmsg2
        acall   pstr
        acall   cin

        lcall   volt5
        mov     dptr, #progmsg1
        acall   pstr
        mov     a, #'5'
        acall   cout
        mov     dptr, #progmsg2
        acall   pstr
        acall   cin

        ret

progmsg1:.db    "Reset now at ",0
progmsg2:.db    " volts, press a key",13,10,0



volt0:  ;drive reset to zero volts
        setb    p1.1
        nop
        clr     p1.2
        lcall   dly_50us
        lcall   dly_50us
        ret

volt5:  ;drive reset to five volts
        setb    p1.1
        setb    p1.2
        lcall   dly_50us
        lcall   dly_50us
        ret


volt12: ;drive reset to twelve volts
        setb    p1.2
        nop
        clr     p1.1
        lcall   dly_50us
        lcall   dly_50us
        ret




        ;approx 0.1 second delay (11.0592 MHz crystal)
delay:  mov     r3, #200
delay2: nop
        mov     r2, #228
        djnz    r2, *
        djnz    r3, delay2
        ret

dly_50us:
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        ret


cin:    jnb     ri, cin
        clr     ri
        mov     a, sbuf
        ret

cout:   jnb     ti, cout        ;not ok to change carry bit here!
        mov     sbuf, a
        clr     ti
        ret

newline:push    acc
        mov     a, #13
        acall   cout
        mov     a, #10
        acall   cout
        pop     acc
        ret

phex:
phex8:
        push    acc
        swap    a
        anl     a, #15
        add     a, #246
        jnc     phex_b
        add     a, #7
phex_b: add     a, #58
        acall   cout
        pop     acc
phex1:  push    acc
        anl     a, #15
        add     a, #246
        jnc     phex_c
        add     a, #7
phex_c: add     a, #58
        acall   cout
        pop     acc
        ret

space:  
        mov     a, #' '
        acall   cout
        ret

PSTR:                  ;print string
        PUSH    ACC
PSTR1:  CLR     A
        MOVC    A,@A+DPTR
        JZ      PSTR2
        mov     c, acc.7
        anl     a, #01111111b
        acall   cout
        Jc      pstr2
        inc     dptr
        SJMP    PSTR1                                          
PSTR2:  POP     ACC
        RET    


poweron:
        setb    p1.0            ;disconnect from the circuit
        setb    p1.1            ;drive reset to 5 volts
        setb    p1.2
        MOV     SP, #stack
        clr     psw.3
        clr     psw.4
        lcall   out             ;take the chip out of the circuit
        mov     th1, #baud_const
        orl     PCON,#10000000b  ; set double baud rate
        MOV     TMOD,#00100001b  ; T0=16 bit, T1=8 bit auto reload
                                 ; both are timers, software control
        MOV     SCON,#01010010b  ; Set Serial for mode 1 &
                                 ; Enable reception, ti=1, ri=0
        ORL     TCON,#01010101b  ; Start both timers, both int are
                                 ; falling edge trigger
        mov     dptr, #welcome
        acall   pstr
        ajmp    main

welcome:.db     "Atmel 89C2051 In-circuit Programmer",13,10
        .db     " - Paul Stoffregen, July 1995",13,10,13,10,0



  ;location of parameter table used by download command
  ;sixteen bytes of internal memory are required
        .equ    dnld_parm, 0x20

;16 byte parameter table: (eight 16 bit values)
;  *   0 = lines received
;  *   1 = bytes received
;  *   2 = bytes written
;  *   3 = bytes unable to write
;  *   4 = incorrect checksums
;  *   5 = unexpected begin of line
;  *   6 = unexpected hex digits (while waiting for bol)
;  *   7 = unexpected non-hex digits (in middle of a line)

dnld:
        mov     dptr, #dnlds1            
        acall   pstr            ;"begin sending file <ESC> to abort"
        mov     r0, #dnld_parm
        mov     r2, #16
dnld0:  mov     @r0, #0         ;initialize all parameters to 0
        inc     r0
        djnz    r2, dnld0

          ;look for begining of line marker ':'
dnld1:  acall   cin
        cjne    a, #27, dnld2   ;Test for escape
        sjmp    dnld_esc
dnld2:  cjne    a, #':', dnld2b
        mov     r1, #0
        acall   dnld_inc
        sjmp    dnld3
dnld2b:   ;check to see if it's a hex digit, error if it is
        lcall   asc2hex
        jc      dnld1
        mov     r1, #6
        acall   dnld_inc
        sjmp    dnld1
          ;begin taking in the line of data
dnld3:  mov     a, #'.'
        acall   cout
        mov     r4, #0          ;r4 will count up checksum
        acall   dnld_ghex
        mov     r0, a           ;R0 = # of data bytes
        mov     r4, a
        acall   dnld_ghex
        mov     dph, a          ;High byte of load address
        add     a, r4
        mov     r4, a
        acall   dnld_ghex
        mov     dpl, a          ;Low byte of load address
        add     a, r4
        mov     r4, a
        acall   dnld_ghex       ;Record type
        mov     r2, a
        add     a, r4
        mov     r4, a
        mov     a, r2
        cjne    a, #1, dnld4    ;End record?
        sjmp    dnld_end
dnld4:
dnld5:  acall   dnld_ghex            ;Get data byte
        mov     r2, a
        mov     r1, #1
        acall   dnld_inc        ;count total data bytes received
        mov     a, r2
        add     a, r4
        mov     r4, a
        mov     a, r2
        lcall   smart_wr        ;c=1 if an error writing
        clr     a
        addc    a, #2
        mov     r1, a
;     2 = bytes written
;     3 = bytes unable to write
        acall   dnld_inc
        inc     dptr
        djnz    r0, dnld5

        acall   dnld_ghex       ;get checksum
        add     a, r4
        jz      dnld1           ;should always add to zero
        mov     r1, #4
        acall   dnld_inc
        sjmp    dnld1

dnld_end:   ;handles the proper end of download marker
        mov     dptr, #dnlds3
        acall   pstr            ;"download went ok..."
        sjmp    dnld_sum

dnld_esc:   ;handle esc received in the download stream
        mov     dptr, #dnlds2    
        acall   pstr            ;"download aborted."
        sjmp    dnld_sum


dnld_inc:     ;increment parameter specified by R1
              ;note, values in Acc and R1 are destroyed
        mov     a, r1
        anl     a, #00000111b   ;just in case
        rl      a
        add     a, #dnld_parm
        mov     r1, a           ;now r1 points to lsb
        inc     @r1
        mov     a, @r1
        jnz     dnldin2
        inc     r1
        inc     @r1
dnldin2:ret

dnld_gp:     ;get parameter, and inc to next one (@r1)
             ;carry clear if parameter is zero.
             ;16 bit value returned in dptr
        setb    c
        mov     dpl, @r1
        inc     r1
        mov     dph, @r1
        inc     r1
        mov     a, dpl
        jnz     dnldgp2
        mov     a, dph
        jnz     dnldgp2
        clr     c
dnldgp2:ret



;a spacial version of ghex just for the download.  Does not
;look for carriage return or backspace.  Handles ESC key by
;poping the return address (I know, nasty, but it saves many
;bytes of code in this 4k ROM) and then jumps to the esc
;key handling.  This ghex doesn't echo characters, and if it
;sees ':', it pops the return and jumps to an error handler
;for ':' in the middle of a line.  Non-hex digits also jump
;to error handlers, depending on which digit.
          
dnld_ghex:
dnldgh1:acall   cin
        lcall   upper
        cjne    a, #27, dnldgh3
dnldgh2:pop     acc
        pop     acc
        sjmp    dnld_esc
dnldgh3:cjne    a, #':', dnldgh5
dnldgh4:mov     r1, #5          ;handle unexpected beginning of line
        acall   dnld_inc
        pop     acc
        pop     acc
        ajmp    dnld3           ;and now we're on a new line!
dnldgh5:lcall   asc2hex
        jnc     dnldgh6
        mov     r1, #7
        acall   dnld_inc
        sjmp    dnldgh1
dnldgh6:mov     r2, a           ;keep first digit in r2
dnldgh7:acall   cin
        lcall   upper
        cjne    a, #27, dnldgh8
        sjmp    dnldgh2
dnldgh8:cjne    a, #':', dnldgh9
        sjmp    dnldgh4
dnldgh9:lcall   asc2hex
        jnc     dnldghA
        mov     r1, #7
        acall   dnld_inc
        sjmp    dnldgh7
dnldghA:xch     a, r2
        swap    a
        orl     a, r2
        ret

;dnlds4 =  "Summary:"
;dnlds5 =  " lines received"
;dnlds6a = " bytes received"
;dnlds6b = " bytes written"

dnld_sum:    ;print out download summary
        mov     dptr, #dnlds4
        acall   pstr
        mov     r1, #dnld_parm
        acall   dnld_gp
        acall   space
        lcall   pint16u
        mov     dptr, #dnlds5
        acall   pstr
        acall   dnld_gp
        acall   space
        lcall   pint16u
        mov     dptr, #dnlds6a
        acall   pstr
        acall   dnld_gp
        acall   space
        lcall   pint16u
        mov     dptr, #dnlds6b
        acall   pstr
dnld_err:    ;now print out error summary
        mov     r2, #5
dnlder2:acall   dnld_gp
        jc      dnlder3         ;any errors?
        djnz    r2, dnlder2
         ;no errors, so we print the nice message
        mov     dptr, #dnlds13
        acall   pstr
        ret
dnlder3:  ;there were errors, so now we print 'em
        mov     dptr, #dnlds7
        acall   pstr
          ;but let's not be nasty... only print if necessary
        mov     r1, #(dnld_parm+6)
        acall   dnld_gp
        jnc     dnlder4
        acall   space
        lcall   pint16u
        mov     dptr, #dnlds8
        acall   pstr
dnlder4:acall   dnld_gp
        jnc     dnlder5
        acall   space
        lcall   pint16u
        mov     dptr, #dnlds9
        acall   pstr
dnlder5:acall   dnld_gp
        jnc     dnlder6
        acall   space
        lcall   pint16u
        mov     dptr, #dnlds10
        acall   pstr
dnlder6:acall   dnld_gp
        jnc     dnlder7
        acall   space
        lcall   pint16u
        mov     dptr, #dnlds11
        acall   pstr
dnlder7:acall   dnld_gp
        jnc     dnlder8
        lcall   pint16u
        mov     dptr, #dnlds12
        acall   pstr
dnlder8:acall   newline
        ret

;dnlds7:  = "Errors:"
;dnlds8:  = " bytes unable to write"
;dnlds9:  = " incorrect checksums"
;dnlds10: = " unexpected begin of line"
;dnlds11: = " unexpected hex digits"
;dnlds12: = " unexpected non-hex digits"
;dnlds13: = "No errors detected"


;a smart-write routine which does a destructive test to
;try to figure out if the memory is SRAM or Flash ROM
;and then does the correct one.  Carry bit will indicate
;if the value was successfully written, C=1 if not written.

smart_wr:
        movx    @dptr, a        ;ok, not so smart just yet
        clr     c
        ret





esc:  ;checks to see if <ESC> is waiting on serial port
      ;C=clear if no <ESC>, C=set if <ESC> pressed
      ;buffer is flushed
        push    acc
        clr     c
        jnb     ri,esc2
        mov     a,sbuf
        cjne    a,#27,esc1
        setb    c
esc1:   clr     ri
esc2:   pop     acc
        ret


pint8u: ;prints the unsigned 8 bit value in Acc in base 10
        push    b
        push    acc
        sjmp    pint8b

pint8:  ;prints the signed 8 bit value in Acc in base 10
        push    b
        push    acc
        jnb     acc.7, pint8b
        mov     a, #'-'
        lcall   cout
        pop     acc
        push    acc
        cpl     a
        add     a, #1
pint8b: mov     b, #100
        div     ab
        jz      pint8c
        add     a, #'0'
        lcall   cout
pint8c: mov     a, b
        mov     b, #10
        div     ab
        jz      pint8d
        add     a, #'0'
        lcall   cout
pint8d: mov     a, b
        add     a, #'0'
        lcall   cout
        pop     acc
        pop     b
        ret


        ;print 16 bit unsigned integer in DPTR, using base 10.
pint16u:        ;warning, destroys r2, r3, r4, r5, psw.5
        push    acc
        mov     a, r0
        push    acc
        clr     psw.5
        mov     r2, dpl
        mov     r3, dph

pint16a:mov     r4, #16         ;ten-thousands digit
        mov     r5, #39
        lcall   pint16x
        jz      pint16b
        add     a, #'0'
        lcall   cout
        setb    psw.5

pint16b:mov     r4, #232        ;thousands digit
        mov     r5, #3
        lcall   pint16x
        jnz     pint16c
        jnb     psw.5, pint16d
pint16c:add     a, #'0'
        lcall   cout
        setb    psw.5

pint16d:mov     r4, #100        ;hundreds digit
        mov     r5, #0
        lcall   pint16x
        jnz     pint16e
        jnb     psw.5, pint16f
pint16e:add     a, #'0'
        lcall   cout
        setb    psw.5

pint16f:mov     a, r2           ;tens digit
        mov     r3, b
        mov     b, #10
        div     ab
        jnz     pint16g
        jnb     psw.5, pint16h
pint16g:add     a, #'0'
        lcall   cout

pint16h:mov     a, b            ;and finally the ones digit
        mov     b, r3
        add     a, #'0'
        lcall   cout

        pop     acc
        mov     r0, a
        pop     acc
        ret

;ok, it's a cpu hog and a nasty way to divide, but this code
;requires only 21 bytes!  Divides r2-r3 by r4-r5 and leaves
;quotient in r2-r3 and returns remainder in acc.  If Intel
;had made a proper divide, then this would be much easier.

pint16x:mov     r0, #0
pint16y:inc     r0
        clr     c
        mov     a, r2
        subb    a, r4
        mov     r2, a
        mov     a, r3
        subb    a, r5
        mov     r3, a
        jnc     pint16y
        dec     r0
        mov     a, r2
        add     a, r4
        mov     r2, a
        mov     a, r3
        addc    a, r5
        mov     r3, a
        mov     a, r0
        ret


upper:  ;converts the ascii code in Acc to uppercase, if it is lowercase
        push    acc 
        clr     c
        subb    a, #97
        jc      upper2          ;is it a lowercase character
        subb    a, #26
        jnc     upper2
        pop     acc
        add     a, #224         ;convert to uppercase
        ret
upper2: pop     acc             ;don't change anything
        ret




pbin:   mov     r0, #8
pbin2:  rlc     a
        mov     f0, c
        push    acc
        mov     a, #'0'
        addc    a, #0
        lcall   cout
        pop     acc
        mov     c, f0
        djnz    r0, pbin2
        rlc     a
        ret


lenstr: mov     r0, #0    ;returns length of a string in r0
        push    acc
lenstr1:clr     a
        movc    a,@a+dptr
        jz      lenstr2
        mov     c,acc.7
        inc     r0
        Jc      lenstr2
        inc     dptr
        sjmp    lenstr1                                          
lenstr2:pop     acc
        ret    


dnlds1: .db     13,10,13,10,"Begin ascii transmission of "
        .db     "Intel HEX format file, "
        .db     "or <ESC> to abort",13,10,13,10,0
dnlds2: .db     13,10,"Download aborted",13,10,13,10,0
dnlds3: .db     13,10,"Download completed",13,10,13,10,0
dnlds4: .db     "Summary:",13,10,0
dnlds5: .db     " lines received",13,10,0
dnlds6a:.db     " bytes received",13,10,0
dnlds6b:.db     " bytes written",13,10,0
dnlds7: .db     "Errors:",13,10,0
dnlds8: .db     " bytes unable to write",13,10,0
dnlds9: .db     " incorrect checksums",13,10,0
dnlds10:.db     " unexpected begin of line",13,10,0
dnlds11:.db     " unexpected hex digits",13,10,0
dnlds12:.db     " unexpected non-hex digits",13,10,0
dnlds13:.db     "No errors detected",13,10,13,10,0


asc2hex:             ;carry set if invalid input
        clr     c
        push    b
        subb    a,#'0'
        mov     b,a
        subb    a,#10
        jc      a2h1
        mov     a,b
        subb    a,#7
        mov     b,a
a2h1:   mov     a,b
        clr     c
        anl     a,#11110000b     ;just in case
        jz      a2h2
        setb    c
a2h2:   mov     a,b
        pop     b
        ret


hexdump:
                        
dump:   
        mov     r2, #16         ;number of lines to print
        lcall   newline
        lcall   newline
dump1:  
        mov     a, r7
        lcall   phex
        mov     a, r6
        lcall   phex
        mov     a,#':'
        lcall   cout
        lcall   space
        mov     r3, #16         ;r3 counts # of bytes to print
        mov     dpl, r6
        mov     dph, r7
dump2:  clr     a
        movc    a, @a+dptr
        inc     dptr
        lcall   phex            ;print each byte in hex
        lcall   space
        djnz    r3, dump2
        lcall   space           ;print a couple extra space
        lcall   space
        mov     r3, #16
        mov     dpl, r6
        mov     dph, r7
dump3:  clr     a
        movc    a, @a+dptr
        inc     dptr
        anl     a, #01111111b   ;avoid unprintable characters
        clr     c
        subb    a, #20h
        jnc     dump4           ;avoid control characters
        mov     a, #(' ' - 20h)
dump4:  add     a, #20h
        lcall   cout
        djnz    r3, dump3
        lcall   newline
        mov     r6, dpl
        mov     r7, dph
        djnz    r2, dump1       ;loop back up to print next line
dump5:  lcall   newline
        ret


