Serout (format # data)

converts a 16-bit number into a counted string for transmission by Serout.

The PBASIC version of Serout has a tremendously convenient feature; the ability to treat data for transmission as either bytes or numbers. The value 65, if sent as a byte, would appear as the letter "A" on the receiving end. If sent as a number, "65" shows up at the receiver. This routine, called BIN_ASC for binary-to-ASCII, converts a pair of bytes (interpreted as a 16-bit number) into text. For example, if the bytes both contain 0FFh, BIN_ASC creates a text string containing "65535" ready for transmission by Serout.

The routine suppresses leading zeros, just as the PBASIC version does. So the number 17 is output as "17" and not "00017".

BIN_ASC goes one step beyond the conversion built into the PBASIC's Serout by allowing you to specify a fixed decimal point in positions 0 through 4 of the text string. Here are examples of the output:
 
 

Fixed Point at:       Value = 1    Value = 65535
     0                  .00001         .65535
     1                  .0001         6.5535
     2                  .001         65.535
     3                  .01         655.35
     4                  .1         6553.5
     5                 1          65535

By setting the fixed point to a value of 5 or greater, you can eliminate it entirely. BIN_ASC can also prepare data for other types of ASCII output devices, such as LCDs or parallel printers. Just write the driver to expect a Serout-style counted string, as described in the previous Serout entry.

The routine uses a short table called decade. Remember that tables and subroutines must be located in the first 256 words of a 512-word program memory page.
 
 

Demonstrating BIN_ASC.

To see BIN_ASC in operation, run it on the PSIM simulator. When the program finishes executing, look at the locations 18h through 1Fh. BIN_ASC will have built a counted string of ASCII characters representing the input value (in decimal) with an inserted decimal point. In the case of the demo, the string will be "655.35" which will display as bytes 36h, 35h, 35h, 2Eh, 33h, and 35h. The string count (6) will appear in location 1Fh.

 For a live demonstration, connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble SEROUT_#.SRC from the accompanying disk and program the PIC or downloader with the assembled program. Make sure to use a 4-MHz clock on the PIC or downloader. Load a terminal program on the PC connected to the PIC and set it for 2400 baud, no parity, 8 data bits, 1 stop bit (N81). When you apply power to the PIC or reset the downloader, the string "655.35" will appear on the PC s creen.


; BIN_ASC 16-bit value
; This routine converts a 16-bit number into a counted string of 
; ASCII characters compatible with Serout. It suppresses 
; leading zeros so that numbers like 17 are sent as "17" not "00017". 

buffer  =       31      ; String buffer at end of memory. 
ASC_0   =       '0'     ; ASCII numbers: 30h thru 39h. 
ASC_dp  =       '.'     ; ASCII char for decimal point (period).
fix     =       3       ; Position for fixed decimal point. 
        
        org     8
dec_no  ds      1       ; Decade (1,10,100...) to work on.
tempH   ds      1       ; 16-bit temporary variable used by
tempL   ds      1       ; the BIN_ASC routine. 
hiB     ds      1       ; MSB of number to convert.
lowB    ds      1       ; LSB of number to convert.
flags   ds      1
zs      =       flags.0 ; Leading-zero suppression flag. 
        device  pic16c55,xt_osc,wdt_off,protect_off
        reset   start
        org     0

decade  jmp     pc+w
        retw    39,16,3,232,0,100,0,10,0,1

start   mov     hiB, #0FFh      ; To see routine's output, either
        mov     lowB, #0FFh     ; use the PSIM PIC simulator, or
        call    BIN_ASC ; merge with Serout. 
        jmp     $       ; Endless loop

; This routine accepts a 16-bit number in hiB, lowB and converts it into 
; a counted ASCII text string located at the address "buffer." The buffer
; grows downward in memory. The first (0th) item in buffer is the number 
; of characters in the string. 

BIN_ASC clr     buffer  ; Clear char count (item 0 in buf).
        clr     flags   ; Clear zs flag. 
        clr     dec_no  ; Clear decade no. 
        mov     fsr,#buffer-1   ; Reserve 0th byte of buffer for count. 
:loop   mov     w,dec_no        ; Get 1st hex digit of decade no. 
        call    decade  ; from the lookup table. 
        mov     tempH,w
        inc     dec_no  ; Get 2nd digit of decade no.
        mov     w,dec_no        ; from the table. 
        call    decade
        mov     tempL,w 
        call    d_point ; Insert decimal point.
        call    sub_it  ; Divide hiB,lowB by tempH,tempL
        mov     w,indirect      ; returning answer in indirect. 
        jb      zs,:no_zs       ; If zs = 0 AND digit = 0 then
        jz      :no_zed ; digit is a leading zero to be
:no_zs  mov     indirect,w      ;  ignored. Otherwise, it's either
        ADD     indirect,#ASC_0 ; an included zero (as in 7501)
        inc     buffer  ; or a non-zero digit. 
        dec     fsr     ; Point to next memory location. 
        setb    zs      ; First non-zero digit sets zs bit. 
:no_zed inc     dec_no  ; Next decade. 
        cse     dec_no,#8       ; If dec_no = 8, we're down to ones. 
        jmp     :loop   ; Otherwise, do next decade. 
        inc     dec_no  ; Update dec_no
        call    d_point ; and call d_point. 
        mov     indirect,lowB   ; Whatever's left belongs in ones.
        ADD     indirect,#ASC_0 ; Add offset for conversion to ASCII. 
        inc     buffer  ; Add 1 to character count. 
        ret
; This routine performs division by iterated subtraction. It is efficient
; in this application because the dividend and divisor keep getting smaller
; as BIN_ASC runs, so the quotient is never larger than nine. A general-
; purpose division routine would be slower (and longer). 

sub_it  clr     indirect        ; Clear to track no. of subtractions.
:loop   sub     lowB,tempL      ; Subtract LSB.
        jc      :skip   ; If no borrow, continue w/MSB. 
        sub     hiB,#1  ; Otherwise borrow from MSB. 
        jc      :skip   ; If borrow causes a carry, then
        inc     hiB     ; add numbers back and return. 
        add     lowB,tempL 
        ret
:skip   sub     hiB,tempH       ; Subtract MSB. 
        jc      :skip2  ; If no borrow, subtract again. 
        ADD     lowB,tempL      ; Otherwise, undo the subtraction
        snc             ; by adding entire 16-bit no. 
        inc     hiB     ; back together and return. 
        ADD     hiB,tempH
        ret
:skip2  inc     indirect        ; No borrow, so do it again. 
        jmp     :loop

; This routine adds a decimal point in the location set by "fix" in the 
; equates at the beginning of the program. The location of the decimal point
; is in front of the "fix"ed digit, numbered starting with 0. If you fix the
; point at 0, the first (0th) character in the string produced by BIN_ASC
; will be a decimal point. If you don't want a decimal point, either move
; it out of range (fix = 6), or delete this routine and the "call d_point"
; in the body of BIN_ASC above. 

d_point cse     dec_no,#fix*2+1 
        ret
        setb    zs
        mov     indirect,#ASC_dp 
        inc     buffer
        dec     fsr
        ret