;Serial I/O routines using the 8051's built-in UART. ;Almost all of these should use CIN and COUT, so they ;could pretty easily be adapted to other devices which ;could have similar single character I/O routines, ;including the 8051's UART using interrupts and buffers ;in memory. ;Much of this code appears in PAULMON1... see the ;PAULMON1.EQU file for an example of how to use some ;of these routines. ;timer reload calculation ; baud_const = 256 - (crystal / (12 * 16 * baud)) .equ baud_const, 252 ;19200 baud w/ 15 MHz ;.equ baud_const, 243 ;4800 baud w/ 12 MHz ;---------------------------------------------------------; ; ; ; Subroutines for serial I/O ; ; ; ;---------------------------------------------------------; cin: jnb ri, cin clr ri mov a, sbuf ret cout: jnb ti, cout clr ti mov sbuf, a ret newline:push acc mov a, #13 acall cout mov a, #10 acall cout pop acc ret ;get 2 digit hex number from serial port ; c = set if ESC pressed, clear otherwise ; psw.5 = set if return w/ no input, clear otherwise ghex: ghex8: clr psw.5 ghex8c: acall cin ;get first digit acall upper cjne a, #27, ghex8f ghex8d: setb c clr a ret ghex8f: cjne a, #13, ghex8h setb psw.5 clr c clr a ret ghex8h: mov r2, a acall asc2hex jc ghex8c xch a, r2 ;r2 will hold hex value of 1st digit acall cout ghex8j: acall cin ;get second digit acall upper cjne a, #27, ghex8k sjmp ghex8d ghex8k: cjne a, #13, ghex8m mov a, r2 clr c ret ghex8m: cjne a, #8, ghex8p ghex8n: acall cout sjmp ghex8c ghex8p: cjne a, #21, ghex8q sjmp ghex8n ghex8q: mov r3, a acall asc2hex jc ghex8j xch a, r3 acall cout mov a, r2 swap a orl a, r3 clr c 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: acall cin acall 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:acall cout acall 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 acall asc2hex jc ghex16c xch a, r5 lcall cout mov a, r5 push acc acall 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 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 PHEX16: PUSH ACC MOV A,DPH ACALL PHEX MOV A,DPL ACALL PHEX POP ACC RET PSTR: ;print string @DPTR 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 ;first we initialize all the registers we can, setting up ;for serial communication. poweron: mov sp, #0x30 clr psw.3 ;set for register bank 0 (init needs it) clr psw.4 orl PCON,#10000000b ; set double baud rate MOV TMOD,#00010001b MOV SCON,#01010000b ; Set Serial for mode 1 & ; Enable reception ORL TCON,#01010010b ; Start timer 1 both timer mov a, #baud_const mov th1, a setb ti ;ti is normally set in this program clr ri ;ri is normally cleared ;jump to main program from here... 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 setb f0 jz pint8c clr f0 add a, #'0' lcall cout pint8c: mov a, b mov b, #10 div ab jnb f0, pint8d jz pint8e pint8d: add a, #'0' lcall cout pint8e: 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 acall pint16x jz pint16b add a, #'0' lcall cout setb psw.5 pint16b:mov r4, #232 ;thousands digit mov r5, #3 acall 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 acall 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 .equ str_buf, 0x20 ;16 byte buffer .equ max_str_len, 19 getstr: ;get a string and store in an internal ram buffer ; str_buf = beginning of the buffer ; max_str_len = max number of char to receive ; (buffer must be one larger for null termination) mov r0, #str_buf gstrz: mov @r0, #0 ;fill buffer with zeros inc r0 cjne r0, #(str_buf+max_str_len+1), gstrz mov r0, #str_buf gstr_in:lcall cin lcall isascii jnc gstr_ctrl cjne r0, #(str_buf+max_str_len), gstradd sjmp gstr_in gstradd:lcall cout mov @r0, a inc r0 sjmp gstr_in gstr_ctrl: cjne a, #13, gstrc2 ;carriage return clr a mov @r0, a ret gstrc2: cjne a, #8, gstrc3 ;backspace gstrbk: cjne r0, #str_buf, gstrbk2 sjmp gstr_in gstrbk2:mov a, #8 lcall cout mov a, #' ' lcall cout mov a, #8 lcall cout dec r0 sjmp gstr_in gstrc3: cjne a, #127, gstrc4 ;delete sjmp gstrbk gstrc4: sjmp gstr_in ;ignore all others pstrbuf: ;print the string in the internal ram buffer mov r0, #str_buf pstrbuf2: mov a, @r0 jz pstrbuf3 lcall cout inc r0 sjmp pstrbuf2 pstrbuf3: ret ;get unsigned integer input to acc gint8u: mov r0, #0 ;r0 holds sum so far mov r1, #0 ;r1 counts number of characters gi8_in: lcall cin mov r2, a ;r2 is temp holding space for input char clr c subb a, #'0' jc gi8_ctrl subb a, #10 jnc gi8_ctrl mov a, r0 mov b, #10 mul ab xch a, b jnz gi8_in mov a, r2 clr c subb a, #'0' add a, b jc gi8_in mov r0, a mov a, r2 lcall cout inc r1 sjmp gi8_in gi8_ctrl: mov a, r2 cjne a, #13, gi8c2 mov a, r0 ret gi8c2: cjne a, #8, gi8c3 gi8bk: cjne r1, #0, gi8bk2 sjmp gi8_in gi8bk2: mov a, #8 lcall cout mov a, #' ' lcall cout mov a, #8 lcall cout mov a, r0 mov b, #10 div ab mov r0, a sjmp gi8_in gi8c3: cjne a, #127, gi8c4 sjmp gi8bk gi8c4: sjmp gi8_in isascii: ;is acc an ascii char, c=1 if yes, c=0 if no push acc cjne a, #0x7F, isasc2 sjmp isasc_no isasc2: anl a, #10000000b jnz isasc_no pop acc push acc anl a, #11100000b jz isasc_no setb c pop acc ret isasc_no: clr c pop acc ret