;In-circuit programmer for the Atmel 89C2051 microcontroller ;Paul Stoffregen, 1995 (paul@ece.orst.edu) ;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. ;uart txd pin of 89C2051 is logically or'd with the txd of the ;programmer while the chip is switch 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 is a BETA test copy, version 0.004. 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 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. ;history: ; ; beta 0.004: Many user interface improvements, .equ statements ; for port pins, buffer address translation added... ; no more hardware memory mapping tricks required now. ; beta 0.003: Everything works, but user interface sucks and ; it may not work in other boards without some hardware tricks ; beta 0.002: Programming works, but it thinks it's failing to ; write. Readback and reading ID bytes works, but erase ; does nothing... good thing the chips come erased when new! ; beta 0.001: Hardware too difficult to manipulate... redo ; hardware using an 82C55 instead. (This code was deleted) ; ;to do list: ; - make all reads from external memory with MOVC ; - do something useful with reading the ID bytes ; - maybe add support for AT89C1051?? ; - add in-circuit option without txd echo enabled. ; - more testing, other boards... need some beta testers. ; - getting very close to 4k size, need to trim down a bit ;****************************************************************** ;Hardware specific configuration options ;****************************************************************** ;timer reload calculation ; baud_const = 256 - (crystal / (12 * 16 * baud)) .equ baud_const, 253 ;19200 baud w/ 11.0592 MHz .equ loc_82c55, 0xE000 ;memory mapped location of the 82C55 ;control signals connected to port pins .equ aic_en, 0x90 ;0=in circuit, 1=disconnect .equ pgmv_pu, 0x91 ;0=pull to 12 volts, 1=float .equ pgmv_pd, 0x92 ;0=pull to 0 volts, 1=float .equ txd_en, 0xB4 ;0=disable txd echo, 1=enable it .equ xtal1_in, 0xB3 ;drives xtal1 pin on 89c2051 .equ osc_en, 0xB5 ;0=disable osc, 1=allow to run ;to the user, a buffer appears to be locatated from 0000, ;but this constant allows the buffer memory to exist in any ;location, since the 87C51's code will exist starting at ;0000. Do not specify a buffer that will wrap beyond FFFF. .equ buffer, 0x2000 ;physical location of the buffer .equ buf_size, 0x0800 ;buffer size, may not be safe to change? ;location to assemble this code (don't overlap with the buffer!) .equ begin, 0x0000 ;****************************************************************** ;it should not be necessary to make changes below this line, ;but you may if you want. Please contact me if you do, thanks. ;****************************************************************** ;memory locations for the 8255 chip .equ port_a, loc_82c55 .equ port_b, loc_82c55+1 .equ port_c, loc_82c55+2 .equ port_pgm, loc_82c55+3 .equ stack, 0x30 .org begin ljmp poweron ;reset vector .org begin+3 ;ext int0 vector .org begin+11 ;timer0 vector .org begin+19 ;ext int1 vector .org begin+27 ;timer1 vector .org begin+35 ;uart vector .org begin+43 ;timer2 vector (8052) .org begin+48 ;finally we can begin ;r6-r7 are used to hold the user's buffer pointer, in terms of the ;physical address, not the 0000-1FFF one the user sees. main: mov r6, #buffer&255 mov r7, #buffer>>8 lcall newline lcall delay clr tr0 lcall erase_buf ajmp mainloop ;the main program loop: mainloop: mov dptr, #mprompt ;print a prompt lcall pstr clr c mov a, r6 ;print buffer offset, not physical subb a, #buffer&255 push acc mov a, r7 subb a, #buffer>>8 lcall phex pop acc lcall phex mov a, #'>' lcall cout lcall cin lcall upper mov dptr, #mainloop ; so routines will return here push dpl push dph main1: cjne a, #'D', main2 ljmp dnld main2: cjne a, #'?', main3 mov dptr, #help ljmp pstr main3: cjne a, #'P', main4 ljmp prog main4: cjne a, #'I', main5 ljmp in main5: cjne a, #'R', main6 ljmp read main6: cjne a, #'H', main7 ljmp hexdump main7: cjne a, #'E', main8 ljmp erase main8: cjne a, #'S', main9 ljmp readid main9: cjne a, #'T', main10 ljmp testpvolt main10: cjne a, #'N', main11 ljmp newloc main11: cjne a, #'U', main12 ljmp upload main12: cjne a, #'C', main13 ljmp comp main13: ret help: .db 13,10 .db "Atmel 89C2051 In-circuit programmer",13,10 .db "BETA VERSION 0.004 (8 Oct 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 " C - compare chip with buffer",13,10 .db " R - read back into ram",13,10 .db " U - upload Intel HEX",13,10 .db " S - read ID bytes",13,10 .db " H - hexdump ram",13,10 .db " N - new memory location",13,10 .db " ? - This help",13,10 .db 0 mprompt:.db 13,10,"AICP 0.004(beta) buf:",0 out: ;switch the 89C2051 out of the circuit ;this should reset the internal address counter too setb aic_en ;disconnect from circuit clr osc_en ;stop the oscillator clr xtal1_in ;force xtal1 to low clr txd_en ;block txd echo lcall volt0 ;make sure reset is low nop nop lcall volt5 ;force reset to high clr txd_en ;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 buffer 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, #buffer 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 xtal1_in ;advance addr counter nop nop nop nop clr xtal1_in mov a, dph cjne a, #(buffer+0x0800)>>8, read1 mov a, dpl cjne a, #(buffer+0x0800)&255, read1 ret comp: mov dptr, #m_comp1 lcall pstr acall compare jnz comp2 mov dptr, #m_comp2 lcall pstr ret comp2: mov dptr, #m_comp3 lcall pstr ret m_comp1:.db 13,10,"Compare Chip with buffer: ", 0 m_comp3:.db "Error, they do not " m_comp2:.db "match",13,10,0 compare: ;compare buffer with chip ;return acc=0 if ok, acc=255 if not equal 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, #buffer compare1: push dpl push dph mov dptr, #port_b movx a, @dptr ;read in data from 89C2051 pop dph pop dpl mov b, a ;put chip's value in b movx a, @dptr ;get memory's value into a cjne a, b, comp_nope inc dptr setb xtal1_in ;advance addr counter nop nop nop nop clr xtal1_in mov a, dph cjne a, #(buffer+0x0800)>>8, compare1 mov a, dpl cjne a, #(buffer+0x0800)&255, compare1 lcall out clr a ret comp_nope: lcall out mov a, #255 ret iserased: ;returns acc=0 if erased, acc=255 if not erased 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, #buffer iser1: push dpl push dph mov dptr, #port_b movx a, @dptr ;read in data from 89C2051 pop dph pop dpl cjne a, #255, iser_no inc dptr setb xtal1_in ;advance addr counter nop nop nop nop clr xtal1_in mov a, dph cjne a, #(buffer+0x0800)>>8, iser1 mov a, dpl cjne a, #(buffer+0x0800)&255, iser1 lcall out clr a ret iser_no: lcall out mov a, #255 ret readid: ;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 xtal1_in ;advance addr counter nop nop nop nop clr xtal1_in mov dptr, #port_b movx a, @dptr ;read in data from 89C2051 lcall phex mov a, #' ' lcall cout setb xtal1_in ;advance addr counter nop nop nop nop clr xtal1_in 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 mov dptr, #m_in1 lcall pstr lcall volt5 ;make sure reset is at 5 volts setb osc_en ;start the oscillator setb xtal1_in ;and allow it to drive the chip setb txd_en ;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 aic_en ;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. in_wait: lcall cin cjne a, #27, in_wait lcall out mov dptr, #m_in2 lcall pstr ret m_in1: .db 13,10,13,10,13,10 .db "In-Circuit, press to remove",13,10,0 m_in2: .db 13,10,"Removed from circuit",13,10,0 prog: ;program the 89C2051 using the data in the buffer lcall iserased jz prog0c ;skip erase if not necessary lcall erase ;do erase first prog0c: lcall out mov dptr, #p_msg1 lcall pstr mov dptr, #buffer 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 xtal1_in nop nop nop clr xtal1_in ;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, #(buffer+0x0800)&255, prog_j mov a, dph cjne a, #(buffer+0x0800)>>8, prog_j lcall newline mov dptr, #p_msg3 lcall pstr lcall compare jnz pgm_bad mov dptr, #p_good lcall pstr ret pgm_bad:mov dptr, #p_bad lcall pstr ;not much we can do at this point though. ret prog_skip: ;don't forget to increment the addr counter when ;we skip a location because the buffer is 0xFF setb xtal1_in nop nop nop clr xtal1_in sjmp pskip2 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 p_msg1: .db 13,10,"Programming Flash ROM:",13,10,0 p_msg2: .db "Loc=",0 p_msg3: .db "Finished Programming",13,10 .db "Verifying...",0 p_good: .db "OK",13,10,0 p_bad: .db "Error, device does not match buffer!",13,10,0 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 13,10,"Erasing Flash ROM...",0 done: .db "done",13,10,0 testpvolt: mov dptr, #testwarn lcall pstr lcall cin lcall upper clr c subb a, #'Y' jz testpv2 lcall newline ret testpv2:lcall newline mov dptr, #twarn2 lcall pstr lcall cin lcall upper clr c subb a, #'Y' jz testpv3 lcall newline ret testpv3: lcall newline lcall volt12 mov dptr, #progmsg1 lcall pstr mov a, #'1' lcall cout mov a, #'2' lcall cout mov dptr, #progmsg2 lcall pstr lcall cin lcall volt0 mov dptr, #progmsg1 lcall pstr mov a, #'0' lcall cout mov dptr, #progmsg2 lcall pstr lcall cin lcall volt5 mov dptr, #progmsg1 lcall pstr mov a, #'5' lcall cout mov dptr, #progmsg2 lcall pstr lcall cin ret testwarn:.db 13,10,"Program Voltage Test. You must remove the",13,10 .db "89C2051 chip or it could be damaged.",13,10,13,10 .db "Are you sure (N/y): ",0 twarn2: .db "Remove the chip, probe pin 1's voltage, " .db "press Y to begin ",0 progmsg1:.db "Reset now at ",0 progmsg2:.db " volts, press a key",13,10,0 volt0: ;drive reset to zero volts setb pgmv_pu nop clr pgmv_pd lcall dly_50us lcall dly_50us ret volt5: ;drive reset to five volts setb pgmv_pu setb pgmv_pd lcall dly_50us lcall dly_50us ret volt12: ;drive reset to twelve volts setb pgmv_pd nop clr pgmv_pu lcall dly_50us lcall dly_50us ret erase_buf: ;fill buffer with 0xFF mov dptr, #buffer ebuf2: mov a, #255 movx @dptr, a inc dptr mov a, dph cjne a, #(buffer+buf_size)>>8, ebuf2 mov a, dpl cjne a, #(buffer+buf_size)&255, ebuf2 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 mov sbuf, a clr ti ret newline:push acc mov a, #13 lcall cout mov a, #10 lcall 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 lcall cout pop acc phex1: push acc anl a, #15 add a, #246 jnc phex_c add a, #7 phex_c: add a, #58 lcall cout pop acc ret PSTR: ;print string PUSH ACC PSTR1: CLR A MOVC A,@A+DPTR JZ PSTR2 mov c, acc.7 anl a, #01111111b lcall cout Jc pstr2 inc dptr SJMP PSTR1 PSTR2: POP ACC RET poweron: setb aic_en ;disconnect from the circuit setb pgmv_pu ;drive reset to 5 volts setb pgmv_pd 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, #help lcall pstr ljmp main translate: ;take value in dptr (0000-1FFF) and translate ;it to a physical address in the buffer mov a, #buffer&255 add a, dpl mov dpl, a mov a, #buffer>>8 addc a, dph mov dph, a ret ;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 lcall pstr ;"begin sending file 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: lcall cin cjne a, #27, dnld2 ;Test for escape sjmp dnld_esc dnld2: cjne a, #':', dnld2b mov r1, #0 lcall 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 lcall dnld_inc sjmp dnld1 ;begin taking in the line of data dnld3: mov a, #'.' lcall cout mov r4, #0 ;r4 will count up checksum lcall dnld_ghex mov r0, a ;R0 = # of data bytes mov r4, a lcall dnld_ghex mov dph, a ;High byte of load address add a, r4 mov r4, a lcall dnld_ghex mov dpl, a ;Low byte of load address add a, r4 mov r4, a lcall translate ;--> translate to physical addesss <-- lcall 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: lcall dnld_ghex ;Get data byte mov r2, a mov r1, #1 lcall 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 lcall dnld_inc inc dptr djnz r0, dnld5 lcall dnld_ghex ;get checksum add a, r4 jz dnld1 ;should always add to zero mov r1, #4 lcall dnld_inc sjmp dnld1 dnld_end: ;handles the proper end of download marker mov dptr, #dnlds3 lcall pstr ;"download went ok..." sjmp dnld_sum dnld_esc: ;handle esc received in the download stream mov dptr, #dnlds2 lcall 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:lcall 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 lcall dnld_inc pop acc pop acc ajmp dnld3 ;and now we're on a new line! dnldgh5:lcall asc2hex jnc dnldgh6 mov r1, #7 lcall dnld_inc sjmp dnldgh1 dnldgh6:mov r2, a ;keep first digit in r2 dnldgh7:lcall cin lcall upper cjne a, #27, dnldgh8 sjmp dnldgh2 dnldgh8:cjne a, #':', dnldgh9 sjmp dnldgh4 dnldgh9:lcall asc2hex jnc dnldghA mov r1, #7 lcall 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 lcall pstr mov r1, #dnld_parm lcall dnld_gp lcall space lcall pint16u mov dptr, #dnlds5 lcall pstr lcall dnld_gp lcall space lcall pint16u mov dptr, #dnlds6a lcall pstr lcall dnld_gp lcall space lcall pint16u mov dptr, #dnlds6b lcall pstr dnld_err: ;now print out error summary mov r2, #5 dnlder2:lcall dnld_gp jc dnlder3 ;any errors? djnz r2, dnlder2 ;no errors, so we print the nice message mov dptr, #dnlds13 lcall pstr ret dnlder3: ;there were errors, so now we print 'em mov dptr, #dnlds7 lcall pstr ;but let's not be nasty... only print if necessary mov r1, #(dnld_parm+6) lcall dnld_gp jnc dnlder4 lcall space lcall pint16u mov dptr, #dnlds8 lcall pstr dnlder4:lcall dnld_gp jnc dnlder5 lcall space lcall pint16u mov dptr, #dnlds9 lcall pstr dnlder5:lcall dnld_gp jnc dnlder6 lcall space lcall pint16u mov dptr, #dnlds10 lcall pstr dnlder6:lcall dnld_gp jnc dnlder7 lcall space lcall pint16u mov dptr, #dnlds11 lcall pstr dnlder7:lcall dnld_gp jnc dnlder8 lcall pint16u mov dptr, #dnlds12 lcall pstr dnlder8:lcall 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 is waiting on serial port ;C=clear if no , C=set if 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 ;carry set if esc pressed ;psw.5 set if return pressed w/ no input ghex16: mov r2, #0 ;start out with 0 mov r3, #0 mov r4, #4 ;number of digits left clr psw.5 ghex16c: lcall cin lcall upper cjne a, #27, ghex16d setb c ;handle esc key clr a mov dph, a mov dpl, a ret ghex16d:cjne a, #8, ghex16f sjmp ghex16k ghex16f:cjne a, #127, ghex16g ;handle backspace ghex16k:cjne r4, #4, ghex16e ;have they entered anything yet? sjmp ghex16c ghex16e:lcall cout lcall ghex16y inc r4 sjmp ghex16c ghex16g:cjne a, #13, ghex16i ;return key mov dph, r3 mov dpl, r2 cjne r4, #4, ghex16h clr a mov dph, a mov dpl, a setb psw.5 ghex16h:clr c ret ghex16i:mov r5, a ;keep copy of original keystroke lcall asc2hex jc ghex16c xch a, r5 lcall cout mov a, r5 push acc lcall ghex16x pop acc add a, r2 mov r2, a clr a addc a, r3 mov r3, a djnz r4, ghex16c clr c mov dpl, r2 mov dph, r3 ret ghex16x: ;multiply r3-r2 by 16 (shift left by 4) mov a, r3 swap a anl a, #11110000b mov r3, a mov a, r2 swap a anl a, #00001111b orl a, r3 mov r3, a mov a, r2 swap a anl a, #11110000b mov r2, a ret ghex16y: ;divide r3-r2 by 16 (shift right by 4) mov a, r2 swap a anl a, #00001111b mov r2, a mov a, r3 swap a anl a, #11110000b orl a, r2 mov r2, a mov a, r3 swap a anl a, #00001111b mov r3, a ret 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 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 phex16: push acc mov a, dph lcall phex mov a, dpl lcall phex pop acc 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 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 space: mov a, #' ' lcall cout ret hexdump: dump: mov r2, #16 ;number of lines to print lcall newline lcall newline dump1: clr c mov a, r6 ;print buffer offset, not physical subb a, #buffer&255 push acc mov a, r7 subb a, #buffer>>8 lcall phex pop acc 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 spaces 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 newloc: lcall newline mov dptr, #prompt6 lcall pstr lcall ghex16 jc nloc2 jb psw.5, nloc2 mov a, dpl add a, #buffer&255 ;adjust to physical buffer location mov r6, a mov a, dph addc a, #buffer>>8 mov r7, a lcall newline ret nloc2: mov dptr, #abort lcall pstr lcall newline ret prompt6:.db "New memory location: ",0 abort: .db " Command Aborted!",13,10+128 upload: lcall get_mem ;assume we've got the beginning address in r3/r2 ;and the final address in r5/r4 (r4=lsb)... ;print out what we'll be doing mov dptr, #uplds3 lcall pstr mov a, r2 clr c subb a, #buffer&255 push acc mov a, r3 subb a, #buffer>>8 lcall phex pop acc lcall phex mov dptr, #uplds4 lcall pstr mov a, r4 clr c subb a, #buffer&255 push acc mov a, r5 subb a, #buffer>>8 lcall phex pop acc lcall phex lcall newline ;need to adjust end location by 1... mov dph, r5 mov dpl, r4 inc dptr mov r4, dpl mov r5, dph mov dptr, #prompt7 lcall pstr lcall cin cjne a, #27, upld2e ajmp abort_it upld2e: lcall newline mov dpl, r2 mov dph, r3 upld3: mov a, r4 ;how many more bytes to output?? clr c subb a, dpl mov r2, a mov a, r5 subb a, dph jnz upld4 ;if >256 left, then do next 16 mov a, r2 jz upld7 ;if we're all done anl a, #11110000b jnz upld4 ;if >= 16 left, then do next 16 sjmp upld5 ;otherwise just finish it off upld4: mov r2, #16 upld5: mov a, #':' ;begin the line lcall cout mov a, r2 lcall phex ;output # of data bytes push dpl push dph clr c mov a, dpl ;adjust output value from physical subb a, #buffer&255 mov dpl, a mov a, dph subb a, #buffer>>8 mov dph, a lcall phex16 ;output memory location mov a, dph add a, dpl add a, r2 mov r3, a ;r3 will become checksum pop dph ;restore physical buffer address pop dpl clr a lcall phex ;output 00 code for data upld6: clr a movc a, @a+dptr lcall phex ;output each byte add a, r3 mov r3, a inc dptr djnz r2, upld6 ;do however many bytes we need mov a, r3 cpl a inc a lcall phex ;and finally the checksum lcall newline lcall esc jnc upld3 ;keep working if no esc pressed sjmp abort_it upld7: mov a, #':' lcall cout clr a lcall phex lcall phex lcall phex inc a lcall phex mov a, #255 lcall phex upld8: lcall newline lcall newline ret uplds3: .db 13,10,13,10,"Sending Intel HEX file from ",0 uplds4: .db " to ",0 pop_it: pop acc pop acc abort_it: lcall newline mov dptr, #abort lcall pstr lcall newline ret prompt7:.db "Press any key: ",0 get_mem: ;this thing gets the begin and end locations for ;a few commands. If an esc or enter w/ no input, ;it pops it's own return and returns to the menu ;(nasty programming, but we need tight code for 4k rom) lcall newline lcall newline mov dptr, #beg_str lcall pstr lcall ghex16 jc pop_it jb psw.5, pop_it push dph push dpl lcall newline mov dptr, #end_str lcall pstr lcall ghex16 mov a, dpl add a, #buffer&255 ;adjust to physical buffer location mov r4, a mov a, dph addc a, #buffer>>8 mov r5, a pop acc add a, #buffer&255 ;adjust to physical buffer location mov r2, a pop acc addc a, #buffer>>8 mov r3, a jc pop_it jb psw.5, pop_it lcall newline ret beg_str:.db "First Location: ",0 end_str:.db "Last location: ",0