{"id":1604,"date":"2006-01-23T21:53:00","date_gmt":"2006-01-24T04:53:00","guid":{"rendered":"http:\/\/www.sheer.us\/wordpress\/?p=1604"},"modified":"2021-05-01T16:13:04","modified_gmt":"2021-05-01T23:13:04","slug":"1604","status":"publish","type":"post","link":"https:\/\/www.sheer.us\/weblogs\/programming\/1604","title":{"rendered":"&#8230;"},"content":{"rendered":"<p>1) I&#8217;ve updated my resume for those of you who care about such things. It&#8217;s at http:\/\/www.sheer.us\/resume\/SheerResume-ascii-wa.txt<\/p>\n<p>2) Below please find a bootloader for a Atmel AVR AtMega8 which accepts a binhex file uploaded via windows terminal (or however else you would like). I&#8217;m releasing this code to the public domain at this point. Feel free to do whatever you like with it, or not, as appropriate. It could probably easily be modified for any AVR CPU<\/p>\n<p>start_bld:<br \/>\n\tcli<br \/>\n\tldi ZL, LOW(RAMEND)<br \/>\n\tldi ZH, HIGH(RAMEND)<br \/>\n\tout SPL, ZL<br \/>\n\tout SPH, ZH\t\t; set up the stack<\/p>\n<p>\trcall init_bld\t\t; set up some hardware stuff<br \/>\n\trcall idle_timer2_bld\t; start timer2 ticking<br \/>\n\trcall set_system_clock_bld\t; setup the system clock for 7.2738Mhz<\/p>\n<p>\t; check and see if there is a serial console present<\/p>\n<p>\tsbis RS232_INVALID_IN_PORT, RS232_INVALID<br \/>\n\trjmp bld_do_boot\t\t\t\t; if no console, boot<\/p>\n<p>\tldi uart_char, XON\t\t\t; send welcome banner<br \/>\n\trcall uart_put_char_bld<br \/>\n\tldi uart_char, &#8216;B&#8217;<br \/>\n\trcall uart_put_char_bld<br \/>\n\tldi uart_char, &#8216;l&#8217;<br \/>\n\trcall uart_put_char_bld<br \/>\n\tldi uart_char, &#8216;d&#8217;<br \/>\n\trcall uart_put_char_bld<br \/>\n\tldi uart_char, &#8216;>&#8217;<br \/>\n\trcall uart_put_char_bld<\/p>\n<p>\trcall uart_get_char_bld_to\t; get a char, or timeout and fall through<br \/>\n\tbrts bld_do_boot<br \/>\n\tcpi uart_char, &#8216;B&#8217;\t\t; if &#8216;B&#8217;, then boot right now<br \/>\n\tbreq bld_do_boot<br \/>\n\tcpi uart_char, &#8216;U&#8217;\t\t; if &#8216;U&#8217;, then go into upload mode<br \/>\n\tbreq bld_do_upload\t\t; todo: authentication<\/p>\n<p>bld_do_boot:<br \/>\n\tjmp ivec_reset<\/p>\n<p>; &#8216;stubbies&#8217; used to make short relative jumps work okay<\/p>\n<p>bld_do_upload_crc_special1:<br \/>\n\trjmp bld_do_upload_crc_special<\/p>\n<p>bld_do_flush1:<br \/>\n\trcall bld_do_flush<br \/>\n\trjmp bld_do_upload<\/p>\n<p>bld_do_upload_err1:<br \/>\n\trjmp bld_do_upload_err<\/p>\n<p>; ******* Upload routine. Decodes lines of Intel Hex, writes them to the flash<br \/>\n; displays a error for bad lines<\/p>\n<p>bld_do_booty:<br \/>\n\t\trcall bld_do_flush<br \/>\n\t\trjmp bld_do_boot<\/p>\n<p>bld_do_upload:<br \/>\n\t; for now, fetch a line, check its checksum, report back good or bad, wash, rinse, repeat<br \/>\n\trcall crlf_bld<br \/>\n\tldi ZL, LOW(STRING_SCRATCH)<br \/>\n\tldi ZH, HIGH(STRING_SCRATCH)<\/p>\n<p>\trcall uart_get_string_bld<br \/>\n\tldi uart_char, XOFF\t\t\t; send XOFF in case this<br \/>\n\trcall uart_put_char_t_bld\t\t; takes a while<br \/>\n\trcall crlf_bld\t\t\t; debugging<br \/>\n\tldi ZL, LOW(STRING_SCRATCH)<br \/>\n\tldi ZH, HIGH(STRING_SCRATCH)<\/p>\n<p>\tclr XL<br \/>\n\tclr sample_cnt<\/p>\n<p>\tld uart_char, Z<br \/>\n\tcpi uart_char, &#8216;:&#8217;<br \/>\n\tbreq bld_do_upload_ok<br \/>\n\tcpi uart_char, &#8216;F&#8217;<br \/>\n\tbreq bld_do_flush1<br \/>\n\tcpi uart_char, &#8216;B&#8217;<br \/>\n\tbreq bld_do_booty<br \/>\n\tcpi uart_char, CR<br \/>\n\tbreq bld_do_upload<br \/>\n\tcpi uart_char, LF<br \/>\n\tbreq bld_do_upload<br \/>\n\trjmp bld_do_upload_err1<br \/>\nbld_do_upload_ok:<br \/>\n\trcall ascii_to_bin_hex_bld\t; convert first 4 bytes of input<br \/>\n\t\t\t\t\t; (should be length and high addr)<\/p>\n<p>\tsts LINE_LEN, TempH\t\t; save length in RAM<br \/>\n\tmov Temp2, TempH\t\t; store byte count in temp register<\/p>\n<p>\tsub XL, TempH<br \/>\n\tsub XL, Temp\t\t\t; compute checksum<\/p>\n<p>\tsts LINE_ADDR_H, Temp\t\t; store high byte of address<\/p>\n<p>\trcall ascii_to_bin_hex_bld\t; convert second 4 bytes of input<\/p>\n<p>\tsts LINE_ADDR_L, TempH\t\t; store low byte of address<br \/>\n\tsts LINE_TYPE, Temp\t\t; store line type<\/p>\n<p>\tsub XL, TempH\t\t\t; compute checksum<br \/>\n\tsub XL, Temp<\/p>\n<p>\t; fetch data, store in line<\/p>\n<p>\t; hopefully data is even, but no guarentee<\/p>\n<p>\tldi YL, LOW(LINE)<br \/>\n\tldi YH, HIGH(LINE)<\/p>\n<p>\tcpi Temp2, 0<br \/>\n\tbreq bld_do_upload\t\t; if zero length record, skip<\/p>\n<p>\tdec Temp2\t\t\t; LENGTH is +1, from dec standpoint<\/p>\n<p>bld_do_upload_loop:<br \/>\n\trcall ascii_to_bin_hex_bld\t\t; translate ascii to binary<\/p>\n<p>\tst Y+, TempH\t\t\t\t; store bytes (high)<br \/>\n\tst Y+, Temp\t\t\t\t; store bytes<br \/>\n\tsub XL, TempH\t\t\t\t; compute checksum<br \/>\n\tsub XL, Temp<\/p>\n<p>\tdec Temp2\t\t\t\t; LEN was given in bytes<br \/>\n\tbreq bld_do_upload_crc_special1\t\t; special case: odd number of bytes<br \/>\n\tdec Temp2\t\t\t\t; but we do words so need 2 decs<br \/>\n\tbrne bld_do_upload_loop\t\t\t; go around again?<\/p>\n<p>\trcall ascii_to_bin_hex_bld\t\t; grab CRC<br \/>\n\tsts LINE_CHECK, Temp\t\t\t; store CRC<br \/>\nbld_do_crc_check:<\/p>\n<p>\tlds TempH, LINE_TYPE<\/p>\n<p>;\tldi uart_char, &#8216;0&#8217;<br \/>\n;\tadd uart_char, TempH<br \/>\n;\trcall uart_put_char_bld<\/p>\n<p>\tcpi TempH, LINETYPE_HDR<br \/>\n\tbreq bld_do_upload<\/p>\n<p>\tcp Temp, XL\t\t\t\t; compare CRC to stored<br \/>\n\tbrne bld_do_upload_err1\t\t\t; if it doesnt&#8217; match, err<\/p>\n<p>\t\t\t\t\t\t; matches, so program part<\/p>\n<p>\tldi YL, LOW(LINE)\t\t\t; use Y ptr to access<br \/>\n\tldi YH, HIGH(LINE)\t\t\t; stored data for this line<br \/>\n\tlds Temp2, LINE_LEN\t\t\t; Temp3 contains countdown<br \/>\n\tlds ZL, LINE_ADDR_L\t\t\t; Z ptr is address in flash<br \/>\n\tlds ZH, LINE_ADDR_H<br \/>\n\tldi XL, LOW(BOOTLOAD_START*2)\t\t; X ptr is max flash addr to write<br \/>\n\tldi XH, HIGH(BOOTLOAD_START*2)\t\t; keeps us from writing bootloader<br \/>\n\t\t\t\t\t\t; which would put is in NWRW state<br \/>\n\t\t\t\t\t\t; and be bad<\/p>\n<p>bld_do_program_loop:<br \/>\n\tcp XL, ZL\t\t\t\t; check to see if we&#8217;re<br \/>\n\tcpc XH, ZH\t\t\t\t; outside of the bootloader<br \/>\n\tbrlo bld_do_program_loop_end\t\t\t; do not program bootloader<br \/>\n\t\t\t\t\t\t; even if it is in file<br \/>\n\tmov TempH, ZH\t\t\t\t; grab the page<br \/>\n\tmov Temp, ZL\t\t\t\t;<br \/>\n\tandi Temp, 0xC0\t\t\t\t; skip off the byte address<br \/>\n\t\t\t\t\t\t; leaving only the page address<\/p>\n<p>\tlds Temp3, LAST_PAGE_L\t\t\t; compare against<br \/>\n\tcp Temp, Temp3\t\t\t\t; the last page that<br \/>\n\tlds Temp3, LAST_PAGE_H\t\t\t; we used and<br \/>\n\tcpc TempH, Temp3\t\t\t; if it&#8217;s different<br \/>\n\tbreq bld_do_program_skip_write\t\t; then we need to write that page<\/p>\n<p>\t\t\t\t\t\t; if we get here, then we must<br \/>\n\tpush ZL\t\t\t\t\t; preserve Z<br \/>\n\tpush ZH<\/p>\n<p>\tlds ZL, LAST_PAGE_L\t\t\t; restore previous Z (page we<br \/>\n\tlds ZH, LAST_PAGE_H\t\t\t; want to write)<\/p>\n<p>\trcall wait_spm_bld\t\t\t; wait for SPM to clear<\/p>\n<p>\tldi Temp, (1<<pgers) + (1<<spmen)\t; erase page\n\tout SPMCR, Temp\n\tspm\n\t\n\trcall wait_spm_bld\t\t\t; wait for SPM to clear\n\t\n\tldi Temp, (1<<pgwrt) + (1<<spmen)\t; write page\n\tout SPMCR, Temp\n\tspm\n\t\n\t; rcall wait_spm_bld\t\t\t; wait for SPM to clear (overkill?)\n\n\tpop ZH\t\t\t\t\t; restore Z\n\tpop ZL\n\t\n\tmov TempH, ZH\t\t\t\t; save this as the new current\n\tmov Temp, ZL\t\t\t\t; page\n\tandi Temp, 0xC0\t\t\t\t; skip off the byte address\n\tsts LAST_PAGE_L, Temp\n\tsts LAST_PAGE_H, TempH\n\t\n\tclr sample_cnt\t\t\t\t; clear counter used to determine\n\t\t\t\t\t\t; if flush is needed at end\n\t\n\tldi uart_char, 'W'\n\trcall uart_put_char_bld\n\t\t\nbld_do_program_skip_write:\n\t\n\trcall wait_spm_bld\t\t\t; wait for SPM to clear\n\tld r0, Y+\t\t\t\t; load from line buffer\n\tld r1, Y+\t\t\t\t; into programming registers\n\tldi Temp, (1<<spmen)\t\t\t; enable SPM\n\tout SPMCR, Temp\t\t\t\t; write data into SPM holding tank\n\tspm\n\t\n\tinc sample_cnt\n\t\t\n\tadiw ZH:ZL, 2\t\t\t\t; increment address\n\t\n\tdec Temp2\t\t\t\t; decrement length\n\tbreq bld_do_program_loop_end\t\t; jump to end if done\n\tdec Temp2\t\t\t\t; need 2x, since wrote 2x\n\tbrne bld_do_program_loop\t\t; fall through if done,\n\t\t\t\t\t\t; otherwise back to top of loop\n\n\t\t\nbld_do_program_loop_end:\n\n\tldi uart_char, '+'\n\trcall uart_put_char_bld\n\trjmp bld_do_upload\n\t\nbld_do_upload_crc_special:\n\t; special case\n\n\trcall ascii_to_bin_hex_bld\t\t; if odd number of bytes\n\tst Y+, TempH\t\t\t\t; then store one byte\n\tsub XL, TempH\t\t\t\t; and calculate checksum\n\t\n\tsts LINE_CHECK, Temp\t\t\t; but grab other byte\n\trjmp bld_do_crc_check\t\t\t; and then do checksum check\n\t\nbld_do_upload_err:\n\trcall crlf_bld\t\t\t\t; send error byte\n\tldi uart_char, 'E'\t\t\t\n\trcall uart_put_char_bld\n\trcall crlf_bld\n\trjmp bld_do_upload\t\t\t; return to accepting transmission\n\nuart_get_char_bld:\n\tsbis\tUCSRA, RXC\n\trjmp\tuart_get_char_bld\t\t; wait for it...\n\tin\tuart_char, UDR\t\t; get it\n;\trcall uart_put_char_bld\t\t\t; echo it (debugging)\n\tret\n\nbld_do_flush:\n\ttst sample_cnt\t\t\t\t; check to see if there is any\n\tbreq bld_do_flush_end\t\t\t; data pending\n\t\n\tpush ZL\n\tpush ZH\n\t\n\tlds ZL, LAST_PAGE_L\t\t\t; restore previous Z (page we \n\tlds ZH, LAST_PAGE_H\t\t\t; want to write)\n\t\n\trcall wait_spm_bld\t\t\t; wait for SPM to clear\n\t\t\n\tldi Temp, (1<<pgers) + (1<<spmen)\t; erase page\n\tout SPMCR, Temp\n\tspm\n\t\n\trcall wait_spm_bld\t\t\t; wait for SPM to clear\n\t\n\tldi Temp, (1<<pgwrt) + (1<<spmen)\t; write page\n\tout SPMCR, Temp\n\tspm\n\t\n\trcall wait_spm_bld\t\t\t; wait for SPM to clear\n\t\n\tpop ZH\n\tpop ZL\n\t\n\tldi uart_char, '+'\n\trcall uart_put_char_bld\t\t\t; indicate to remote host that\n\t\t\t\t\t\t; flush was neccesary\n\tret\n\t\nbld_do_flush_end:\n\tldi uart_char, '-'\t\t\t; indicate to remote host that\n\trcall uart_put_char_bld\t\t\t; flush was *not* neccesary\n\tret\n\t\t\nuart_get_char_bld_to:\t\t\t\t; get data from UART, with timeout\n\tclr Temp\n\tclr XH\n\tclr XL\n\tclt\nuart_get_char_bld_to_loop:\n\tsbic\tUCSRA, RXC\n\trjmp uart_get_char_bld_to_done\n\t\n\tdec Temp\n\tbrne uart_get_char_bld_to_loop\n\tdec XH\n\tbrne uart_get_char_bld_to_loop\n\tdec XL\n\tbrne uart_get_char_bld_to_loop\n\tset\n\tret\n\t\nuart_get_char_bld_to_done:\n\tin uart_char, UDR\n\tret\n\t\n\t\nuart_put_char_bld:\n\tsbis\tUCSRA, UDRE\t\t; wait for space\n\trjmp\tuart_put_char_bld\n\tout\tUDR, uart_char\n\tret\n\n\n\ninit_bld:\n\tldi Temp, (1<<ursel) + (1<<ucsz1) + (1<<ucsz0)\n\tout UCSRC, Temp\t\t\t\t\t; configure UART for async\n\tclr Temp\n\t\n\tldi\ttemp, 3\t\t\t\t\t; 115 kbauds\n\tout\tUBRRL, temp\n\t\n\tldi\ttemp, (1<<rxen) | ( 1<<txen) | (1<<rxcie)\t\t; turn on TX &#038; RX\n\tout\tUCSRB, temp\n\tclr Temp\n\tsts LAST_PAGE_L, Temp\n\tsts LAST_PAGE_H, TEmp\n\tout\tUBRRH, temp\n\tret\t; party on, wayne! Party on, Garth!\n\nidle_timer2_bld:\n\tpush temp\n\tin Temp, ASSR\t\t\t; enable async operation\n\tsbr Temp, (1<<as2)\n\tout ASSR, Temp\n\tldi Temp, (1<<cs20) + (1<<ctc2)\t\t; no output scaling\n\tout TCCR2, Temp\n\tclr Temp\n\t\nidle_timer2_cnt_bld:\n\tdec Temp\n\tbrne idle_timer2_cnt_bld\n\t\nidle_timer2_init_bld:\t\n\tin\ttemp, ASSR\t\t\t; code courtesy of Bruce Sherry\n\tsbrc\ttemp, TCN2UB\t\t\t; this waits for Timer2 to stablize\n\trjmp\tidle_timer2_init_bld\t\t\n\n\tpop Temp\n\tret\n\n\nmeasure_clock_bld:\n\t; This routine will measure the CPU clock\n\t; against Timer2\n\t\n\t\n\t; timer1 is set in \/8 mode\n\t; timer2 is set in native mode\n\t; Timer2 is placed in free running mode and reset to zero\n\t; Timer1 is placed in free running mode and reset to zero\n\t\n\t; wait for timer2 to overflow (32768 \/ 256)\n\tpush Temp3\n\tpush TempH\t\n\tpush Temp\n\tin Temp, SREG\n\tpush Temp\n\tin Temp, TCCR2\n\tpush Temp\n\tin Temp, TCCR1B\n\tpush Temp\n\t\n\n\tcli\t; kill ALL interrupts\n\tin Temp, TIFR\n\tsbr Temp, (1<<tov2)\n\tout TIFR, Temp\n\t\n\tclr Temp3\n\tout TCNT1H, Temp3\n\tout TCNT1L, Temp3\n\tout TCNT2, Temp3\n\t\n\tldi Temp, (1<<cs20)\n\tldi TempH, (1<<cs11)\n\t\n\tout TCCR2, Temp\n\tout TCCR1B, TempH \t; drop the hammer\n\n\t\nmeasure_clock_go_bld:\n\tin Temp, TIFR\n\tsbrs Temp, TOV2\n\trjmp measure_clock_go_bld\n\t\n\tin Temp, TCNT1L\n\tin TempH, TCNT1H\n\t\n\tsts CLOCK_MEASURE_L, Temp\n\tsts CLOCK_MEASURE_H, TempH\n\t\n\tpop Temp\n\tout TCCR1B, Temp\n\tpop Temp\n\tout TCCR2, Temp\n\tpop Temp\n\tout SREG, Temp\n\tpop Temp\n\tpop TempH\n\tpop Temp3\t\n\tret\n\nset_system_clock_bld:\n\n\t; try to target 7200\n\t; atmel did a binary sweep, but we do a slower and less\n\t; reliable upward rise because it's simpler\n\t\n\tin Temp3, ASSR\n\tsbr Temp3, (1<<as2)\t\t; probably not needed any more\n\tout ASSR, Temp3\n\t\n\tldi YL, LOW(CAL_VALUE)\n\tldi YH, HIGH(CAL_VALUE)\n\t\n\tldi Temp3, CLOCK_SEED_VALUE\t; reasonable seed value ?\n\tclr LoTemp\n\t\nset_system_clock_loop_bld:\n\tout OSCCAL, Temp3\t\t; write our current guess to the \n\t\t\t\t\t; RC calibration register\n\nset_system_clock_loop2_bld:\n\tdec LoTemp\t\t\t; wait 256 ticks\n\tbrne set_system_clock_loop2_bld\t; for the thing to stablize.\n\t\n\trcall measure_clock_bld\t\t; measure current sysclock\n\t\n\tlds Temp, CLOCK_MEASURE_L\t; load the measured values\n\tlds TempH, CLOCK_MEASURE_H\n\t\n\tcp Temp, YL\t\t\t; compare them against the\n\tcpc TempH, YH\t\t\t; desired values\n\tbrsh set_system_clock_done_bld\t; if we're there, then return\n\t\n\tinc Temp3\t\t\t; otherwise, increase the trial value\n\trjmp set_system_clock_loop_bld\t; and try again\n\nset_system_clock_done_bld:\t\n\tsts CLOCK_CAL_VALUE, Temp3\t; save in RAM for debugging purposes\n\tret\t\t\t\t; and return\n\nuart_get_string_bld:\n\t\t\t\t\t; gets a string, stores it @ ZH:ZL\n\t\t\t\t\t; null terminates it\n\t\t\t\t\t; end of string is detected as CR\/LF\n\tldi uart_char, '$'\t\t\n\trcall uart_put_char_bld\n\tldi uart_char, XON\t\t\t; tell other end to start\n\trcall uart_put_char_bld\t\t; sending data again\n\t\nuart_get_loop_bld:\n\trcall uart_get_char_bld\n\tcpi uart_char, CR\t\t; if carrage return, done\n\tbreq uart_get_done_bld\n\tcpi uart_char, LF\t\t; if linefeed, done\n\tbreq uart_get_done_bld\n\tst Z+, uart_char\t\t; store wherever the Z pointer is pointing\n\trjmp uart_get_loop_bld\n\t\nuart_get_done_bld:\nterminate_string_bld:\n\tclr uart_char\t\t\t; terminate string\n\tst Z, uart_char\n\tret\n\nascii_to_bin_hex_bld:\n\t; load the iterator and clear the storage tank\n\tpush XH\n\tpush XL\n\tpush temp2\n\tpush YL\n\tpush YH\n\t\n\tldi XL, 4\n\tclr YL\n\tclr YH\n\t\t\n\t; find the first digit\nascii_to_bin_hex_start_loop_bld:\n\tld Temp, Z\t\t\t; first, check to see\n\ttst Temp\n\tbreq ascii_to_bin_hex_end_bld\t; is null?\n\tldi TempH, '0'\t\t\t; if it is digit\n\tsub Temp, TempH\t\t\t; \n\tcpi Temp, 10\t\t\t; Check some more\n\tbrlo ascii_to_bin_hex_loop_bld\t; is it digit?\n\tld temp, Z\t\t\t; load again\n\tandi Temp, 0x5F\n\tldi TempH, 'A'\t\t\t; is it hex?\n\tsub Temp, TempH\t\t\t\n\tcpi Temp, 7\t\t\t; is it hex\n\tbrlo ascii_to_bin_hex_loop_bld\n\t\n\tadiw ZH:ZL, 1\t\t\t; No? Increment 1 and try again\n\trjmp ascii_to_bin_hex_start_loop_bld\n\t\t\nascii_to_bin_hex_loop_bld:\n\t\n\tld XH, Z\t\t\t; load current digit into temp\n\ttst XH\t\t\t; check to see if it's zero (null)\n\tbreq ascii_to_bin_hex_end_bld\n\n\t\nascii_to_bin_hex_step_bld:\n\n\t; This is a little more complicated than the straight case\n\t; because we need to determine if the digit is 0-9 or A-F\n\t\n\t\n\tldi TempH, '0'\t; The basic idea here is that for each digit we read,\n\t\t\t; we subtract '0' from it..\n\tsub XH, TempH\t; try and see if it is 0-9\n\tcpi XH, 10\t; <10? \n\tbrlo ascii_to_bin_hex_cont_bld\t; then do your stuff\n\t\n\tld XH, Z\t\t; if not, load again\n\tandi XH, 0x5F\t\t; make uppercase (Thanks, Bruce Sherry)\n\tldi TempH, 'A'\t\t; try with a 'A'\n\tsub XH, TempH\t\t; subtract\n\tldi TempH, 10\t\t; add 10 to get correct value\n\tadd XH, TempH\t\t; add 10 to get correct value\n\tcpi XH, 16\t\t; compare with 16\n\tbrsh ascii_to_bin_hex_end_bld ; >=? Then non-digit char, end<\/p>\n<p>ascii_to_bin_hex_cont_bld:<\/p>\n<p>\tadiw ZH:ZL, 1<br \/>\n\tmovw mp16uH:mp16uL, YH:YL<\/p>\n<p>\tldi mc16uL, 16\t; load 16<br \/>\n\tclr mc16uh\t; into multiplier<\/p>\n<p>\trcall mpy16u_bld\t; and multiply<\/p>\n<p>\tadd m16u0, XH\t; Add current value to stack<br \/>\n\tclr XH<br \/>\n\tadc m16u1, XH\t; carry as needed<\/p>\n<p>\tmovw YH:YL, m16u1:m16u0<\/p>\n<p>\tdec XL\t\t\t; This keeps us from<br \/>\n\tbrne ascii_to_bin_hex_loop_bld<\/p>\n<p>ascii_to_bin_hex_end_bld:<br \/>\n\tmovw TempH:Temp, YH:YL<br \/>\n\tpop YH<br \/>\n\tpop YL<br \/>\n\tpop Temp2<br \/>\n\tpop XL<br \/>\n\tpop XH<br \/>\n\tret<\/p>\n<p>uart_put_string_bld:<br \/>\n\t; put a null-terminated string to the uart<br \/>\n\t; Z should be pointing to the beginning of the string in RAM<br \/>\n\tclr uart_char<br \/>\n\tst Z, uart_char<br \/>\n\tldi ZL, LOW(STRING_SCRATCH)<br \/>\n\tldi ZH, HIGH(STRING_SCRATCH)<\/p>\n<p>uart_put_string_loop_bld:<br \/>\n\tld uart_char, Z+<br \/>\n\tcpi uart_char, 0<br \/>\n\tbreq uart_put_string_done_bld<br \/>\n\trcall uart_put_char_bld<br \/>\n\trjmp uart_put_string_loop_bld<\/p>\n<p>uart_put_string_done_bld:<br \/>\n\tret<\/p>\n<p>crlf_bld:<br \/>\n\tldi uart_char, CR<br \/>\n\trcall uart_put_char_bld<br \/>\n\tldi uart_char, LF<br \/>\n\trcall uart_put_char_bld<br \/>\n\tret<\/p>\n<p>uart_put_char_t_bld:<br \/>\n\tsbis RS232_INVALID_IN_PORT, RS232_INVALID<br \/>\n\trjmp uart_put_char_t_done_bld<\/p>\n<p>\tpush Temp<br \/>\n\tin Temp, UCSRA<br \/>\n\tsbr Temp, (1<<txc)\n\tout UCSRA, Temp\n\t\nuart_put_char_t1_bld:\n\tsbis\tUCSRA, UDRE\t; wait for space\n\trjmp\tuart_put_char_t1_bld\n\tout\tUDR, uart_char\nuart_put_char_t2_bld:\t\t; I'll be ba-ack\n\tsbis\tUCSRA, TXC\t; wait for complete transmission\n\trjmp uart_put_char_t2_bld\n\tpop Temp\nuart_put_char_t_done_bld:\n\tret\n\ndiv16u_bld:\tclr\tdrem16uL\t;clear remainder Low byte\n\tsub\tdrem16uH,drem16uH;clear remainder High byte and carry\n\tldi\tdcnt16u,17\t;init loop counter\nd16u_1_bld:\trol\tdd16uL\t\t;shift left dividend\n\trol\tdd16uH\n\tdec\tdcnt16u\t\t;decrement counter\n\tbrne\td16u_2_bld\t\t;if done\n\tret\t\t\t;    return\nd16u_2_bld:\trol\tdrem16uL\t;shift dividend into remainder\n\trol\tdrem16uH\n\tsub\tdrem16uL,dv16uL\t;remainder = remainder - divisor\n\tsbc\tdrem16uH,dv16uH\t;\n\tbrcc\td16u_3_bld\t\t;if result negative\n\tadd\tdrem16uL,dv16uL\t;    restore remainder\n\tadc\tdrem16uH,dv16uH\n\tclc\t\t\t;    clear carry to be shifted into result\n\trjmp\td16u_1_bld\t\t;else\nd16u_3_bld:\tsec\t\t\t;    set carry to be shifted into result\n\trjmp\td16u_1_bld\n\n\nmpy16u_bld:\n\tmul\tmc16uL, mp16uL\n\tmovw\tm16u1:m16u0, r1:r0\n\tmul\tmc16uH, mp16uL\t\t; ah * bl\n\tadd\tm16u1, r0\n\tmul \tmp16uH, mc16uL\n\tadd \tm16u1, r0\n\tret\n\nwait_spm_bld:\n\tpush Temp\nwait_spm_bld_loop:\n\tin Temp, SPMCR\t\t\t\t\n\tsbrc Temp, SPMEN\t\t\t\n\trjmp wait_spm_bld_loop\n\tpop Temp\n\tret\n\t\n\n<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1) I&#8217;ve updated my resume for those of you who care about such things. It&#8217;s at http:\/\/www.sheer.us\/resume\/SheerResume-ascii-wa.txt 2) Below please find a bootloader for a Atmel AVR AtMega8 which accepts a binhex file uploaded via windows terminal (or however else you would like). I&#8217;m releasing this code to the public domain at this point. Feel [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"_links":{"self":[{"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/posts\/1604"}],"collection":[{"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/comments?post=1604"}],"version-history":[{"count":2,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/posts\/1604\/revisions"}],"predecessor-version":[{"id":4091,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/posts\/1604\/revisions\/4091"}],"wp:attachment":[{"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/media?parent=1604"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/categories?post=1604"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/tags?post=1604"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}