Copied with permission from http://home.clear.net.nz/pages/joecolquitt/text2lcd.html
This page is a companion to
LCD BMP at
PICList and uses the same board and schematic
NOTE : - the routines here are not examples of compact code. They are intended
to show general methods and help the user understand how these graphic screens
work to develop their own s/w for line drawing, animations etc. For instance,
routines could be optimised as general modules which can receive a variety
of values, such as the result of an ADC conversion, x,y positions, scrolling
etc, rather than these hard-coded examples.
In these example routines, character data is stored in PIC program memory
and fetched with the table commands. It could be stored in other memory,
like an external EEPROM, and fetched with eg I2C or SPI. This may suit some
applications or micros better.
The examples given here are based on containing data within discrete pages.
However, with a little manipulation, data can be split so that eg an 8-bit
character straddles two pages.
For example, the top half of a character can be in the lower half of Page
2, and the lower half of the character goes into the top half of Page 3
Using Boolean operators (eg AND) and shifts of a character's shape data,
it can be placed anywhere and also merged with another for animations etc
and line drawings which, for example, cross.
The display memory can be read, enabling one character to 'pass through'
another by using logic such as inclusive-ORing (IOR) and AND-ing
The scrolling register, line_base ; would be unchanged for a static display.
However, putting line_base in a 0-64 loop will demonstrate what it does.
Any text on the screen moves upwards as line_base is increased (and conversely
will move down as line_base is decreased)
It will wrap-around and re-appear at the bottom of the screen, stopping at
its original position when line_base = 64 (actually 0 again, as the argument
of line_base won't reach into bits 6 and 7)
As well as the text/picture moving, the pages also move. In this routine,
the right-hand side of the screen scrolls upwards 8 lines. Page 0 rolls out
of the top of the screen and is now at the bottom of the screen, with Page
7 above it. The small box drawn in Page 0 is therefore at the bottom of the
screen, immediately to the right of centre.
bcf cs1 ;enable right-hand controller usec bsf cs2 usec clrf temp0 ;scroll counter scroll movfw temp0 addlw line_base call write_c call ms100 ;100ms delay, so scroll can be seen incf temp0 ;increment, test for > 8 movlw .8 cpfsgt temp0 ;yes bra scroll ;no, loop ;print position for box movlw y_base+.0 ;column 0 call write_c movlw page_base+.0 ;Page 0 call write_c movlw b'11100011' ;box with a ding in it call write_d movlw b'10000001' call write_d movlw b'10011001' call write_d movlw b'10000001' call write_d movlw b'10000001' call write_d movlw b'11111111' call write_d
By introducing new display data, the 128x64 area can appear to scroll up/down
through a much longer screen. Perhaps showing more menu options, or scrolling
to a new screen as an alternative to clearing and printing one.
Note that this screen has two controllers, and is really two 64x64 screens
that can be scrolled independently in either direction, so more than whole-screen
effects are possible.
------------------------------------------------------------------------------
As explained in the companion page, these LCDs display a data byte as a vertical
column.
Here's the shape (taken from the HD44780 datasheet) of a lower-case 't' which
is drawn rotated right, and its 5 bytes of data compiled in that orientation,
to become 5 columns.
------------------------------------------------------------------------------
;PIC (18F) set-up #define do_alt flags,0 ;= 0 standard, = 1 descenders ;per KS0108 datasheet ;page_base = b'10111000' ;b8 + 0 - 7 (Page number) ;y_base = b'01000000' ;40 + address 0 - 63 (Column number) ;line_base = b'11000000' ;c0 + line 0 - 63 (Scrolling) page_base = 0xb8 y_base = 0x40 line_base = 0xc0 rom_ram macro var ;macro to copy string from ROM to RAM movlw upper(var) ;load TBLPTR with string address movwf tblptru movlw high(var) movwf tblptrh movlw low(var) movwf tblptrl tblrd*+ call get_txt endm dispw macro ;display WREG call write_d endm ;two arrays cblock 0x0040 str_temp:32 ;RAM copy of string in ROM endc cblock 0x0060 chars:32 ;descender/not descender endc ;Arbitrary data storage addresses in PIC program memory strings = 0x1000 ch16 = 0x1200 asc5x7 = ch16+0x800 movlw 0x00 ;initialise scroll register addlw line_base call write_c ;----------------
Here are two examples of text. The top line is standard 5 x 7, as per a typical
alphanumeric (eg 16 x 2) LCD. The lower line shows the same with alternate
descender characters used instead
The routine below is in two sections, Pass 1 and Pass 2
The method I've chosen, because on this PIC I think it's as good as any other,
is (if the do_alt flag is set) to note during Pass 1 which characters will
be replaced with alternates.
Assuming do_alt is set -
After the rom_ram jazz macro has executed, a copy of the string is in a RAM
array at 0x0040
Before a character is printed in Pass 1, it's tested to see if it's a descender
character that can be replaced. If it is, then its standard ASCII number
is substituted by the alternate drawing. The top half of the character is
drawn in Pass 1 and the alternate ASCII is recorded in a separate array (at
0x0060) for printing the lower half of the character in Pass 2. If it isn't
a replaceable descender character, then a space (ie blank character) is
recorded
So, after Pass 1 j z z , g y p have been found, replaced, top halves drawn,
and the array created for Pass 2 drawing.
a E and t do not have any descender data, spaces are recorded for their lower
halves
;Display standard 16x2 characters, with optional alternate descender ;characters using extra data and a page lower ; ;If displaying descenders (or characters more than 8 pixels high) ; ;Test character to be displayed. If not a descender character ;then store 0x20 (space) at its position in an arrary. If it is a ;descender character, then store the alternate character number ;at its position in the array alt_ch lfsr fsr0,chars ;for Pass 2 array, 0x20's and 128+ lfsr fsr1,str_temp ;RAM copy of ROM string rom_ram jazz ;copy "jazz,Egypt" string from ROM to RAM clrf postinc1 ;add trailing 0 lfsr fsr1,str_temp ;reset RAM string pointer ;---------------- ;Pass 1, display character (optionally the top half of a tall character) pass1 movlw low(asc5x7) ;5x7 character data base address movwf tblptrl movlw high(asc5x7) movwf tblptrh clrf tblptru movfw postinc1 ;get character from string in RAM skpnz ;exit if 0x00, ie end of string goto pass2 ;Pass 1 complete ;---------------- ;Optional, note any descender characters for Pass 2 call test_alt ;test if a descender character movff alt_char,postinc0 ;store result in array for second pass ;---------------- addlw -0x20 ;subtract 0x20 from W mullw .10 ;x 5 x 2 = offset from base address ;ie 5 words per character movfw prodl ;add offset to base address addwf tblptrl movfw prodh addwfc tblptrh movlw .5 ;number of data bytes to retrieve movwf temp0 movlw 0x00 ;display a blank leading column call write_d p1_data tblrd*+ ;fetch low byte of data word movff tablat,wreg call write_d ;display it tblrd*+ ;fetch high byte of data word ;but ignore data for Pass 1 decfsz temp0 bra p1_data ;loop for 5 bytes bra pass1 ;loop until end of string ;---------------- ;Pass 2. Reset display position, display space or more data pass2 btfss do_alt ;Pass 2 if doing descenders return ;or exit for standard characters movfw column addlw y_base ;reset to column 10 call write_c movfw page_copy ;next page down from Pass 1 call write_c lfsr fsr0,chars ;array, spaces and descender characters next_p2 movlw low(asc5x7) ;5x7 character data base address movwf tblptrl movlw high(asc5x7) movwf tblptrh clrf tblptru movfw postinc0 ;get character from string in RAM skpnz ;exit if 0x00, ie end of string return addlw -0x20 ;offset mullw .10 movfw prodl ;add offset to base address addwf tblptrl movfw prodh addwfc tblptrh movlw .5 ;number of data bytes to retrieve movwf temp0 movlw 0x00 ;display a blank leading column call write_d p2_data tblrd*+ ;fetch low byte of data word ;ignore it for Pass 2 tblrd*+ ;fetch high byte movff tablat,wreg call write_d ;display it decfsz temp0 bra p2_data ;loop for 5 bytes bra next_p2 ;next character ;---------------- ;test character for substitution by one with a true descender test_alt btfss do_alt ;exit if do_alt = 0 return ;ie do not detect and replace movwf temp0 ;temp store character movlw .128 ;alternate character base code movwf alt_char ;test for , g j p q y or z movfw temp0 xorlw "," bz ch128 ;is , xorlw ","^"g" bz ch129 ;is g xorlw "g"^"j" bz ch130 ;is j xorlw "j"^"p" bz ch131 ;is p xorlw "p"^"q" bz ch132 ;is q xorlw "q"^"y" bz ch133 ;is y xorlw "y"^"z" bz ch134 ;is z movlw 0x20 ;ASCII space movwf alt_char ;not descender, store lower-half space movfw temp0 ;and restore character to W return ;increment alt_char to match character's ASCII ch134 incf alt_char ;ASCII code for alternate character ch133 incf alt_char ch132 incf alt_char ch131 incf alt_char ch130 incf alt_char ch129 incf alt_char ch128 nop movfw alt_char ;replace standard with alternate return ;================================================ ; Copy a text string ;================================================ get_txt movfw tablat ;get characters until btfsc wreg,7 ;W > 0x7f (ie FF terminator) return movwf postinc1 ;store in RAM tblrd*+ bra get_txt ;================================================ ; Data ;================================================ org strings jazz db "jazz,Egypt",0xff v_str db "Voltage",0xff ;------------ org ch16 ;Standard LCD numbers, twice size, drawn rotated right ;(the sharp-eyed will notice the data repetition)
dw 0x0ffc,0x0ffc,0x3303,0x3303,0x30c3,0x30c3,0x3033,0x3033,0x0ffc,0x0ffc ;0 dw 0x0000,0x0000,0x300c,0x300c,0x3fff,0x3fff,0x3000,0x3000,0x0000,0x0000 ;1 dw 0x300c,0x300c,0x3c03,0x3c03,0x3303,0x3303,0x30c3,0x30c3,0x303c,0x303c ;2 dw 0x0c03,0x0c03,0x3003,0x3003,0x3033,0x3033,0x30cf,0x30cf,0x0f03,0x0f03 ;3 dw 0x03c0,0x03c0,0x0330,0x0330,0x030c,0x030c,0x3fff,0x3fff,0x0300,0x0300 ;4 dw 0x0c3f,0x0c3f,0x3033,0x3033,0x3033,0x3033,0x3033,0x3033,0x0fc3,0x0fc3 ;5 dw 0x0ff0,0x0ff0,0x30cc,0x30cc,0x30c3,0x30c3,0x30c3,0x30c3,0x0f00,0x0f00 ;6 dw 0x0003,0x0003,0x3f03,0x3f03,0x00c3,0x00c3,0x0033,0x0033,0x000f,0x000f ;7 dw 0x0f3c,0x0f3c,0x30c3,0x30c3,0x30c3,0x30c3,0x30c3,0x30c3,0x0f3c,0x0f3c ;8 dw 0x003c,0x003c,0x30c3,0x30c3,0x30c3,0x30c3,0x0cc3,0x0cc3,0x03fc,0x03fc ;9 ;------------ org asc5x7 ;5x7 ASCII set without descenders ;Standard LCD characters, normal size, drawn rotated right
8-bit x 5 '0'
16-bit x 5 '0'
16-bit x 5 'g', including descender in upper byte
;16-bit data, can ignore leading 00 if descenders not needed ; ;ASCII 0x20 - 0x2f, d32 - d47 dw 0x0000,0x0000,0x0000,0x0000,0x0000 ;space dw 0x0000,0x0000,0x005f,0x0000,0x0000 ;! dw 0x0000,0x0007,0x0000,0x0007,0x0000 ;" dw 0x0014,0x007f,0x0014,0x007f,0x0014 ;# dw 0x0024,0x002a,0x007f,0x002a,0x0012 ;$ dw 0x0023,0x0013,0x0008,0x0064,0x0062 ;% dw 0x0036,0x0049,0x0055,0x0022,0x0050 ;& dw 0x0000,0x0005,0x0003,0x0000,0x0000 ;' dw 0x0000,0x001c,0x0022,0x0041,0x0000 ;( dw 0x0000,0x0041,0x0022,0x001c,0x0000 ;) dw 0x0014,0x0008,0x003e,0x0008,0x0014 ;* dw 0x0008,0x0008,0x003e,0x0008,0x0008 ;+ dw 0x0000,0x0050,0x0030,0x0000,0x0000 ;, dw 0x0008,0x0008,0x0008,0x0008,0x0008 ;- dw 0x0000,0x0060,0x0060,0x0000,0x0000 ;. dw 0x0020,0x0010,0x0008,0x0004,0x0002 ;/ ;ASCII 30 - 3f, 48 - 63 dw 0x003e,0x0051,0x0049,0x0045,0x003e ;0 dw 0x0000,0x0042,0x007f,0x0040,0x0000 ;1 dw 0x0042,0x0061,0x0051,0x0049,0x0046 ;2 dw 0x0021,0x0041,0x0045,0x004b,0x0031 ;3 dw 0x0018,0x0014,0x0012,0x007f,0x0010 ;4 dw 0x0027,0x0045,0x0045,0x0045,0x0039 ;5 dw 0x003c,0x004a,0x0049,0x0049,0x0030 ;6 dw 0x0001,0x0079,0x0005,0x0003,0x0001 ;7 dw 0x0036,0x0049,0x0049,0x0049,0x0036 ;8 dw 0x0006,0x0049,0x0049,0x0029,0x001e ;9 dw 0x0000,0x0036,0x0036,0x0000,0x0000 ;: dw 0x0000,0x0056,0x0036,0x0000,0x0000 ;; dw 0x0008,0x0014,0x0022,0x0041,0x0000 ;< dw 0x0014,0x0014,0x0014,0x0014,0x0014 ;= dw 0x0000,0x0041,0x0022,0x0014,0x0008 ;> dw 0x0002,0x0001,0x0051,0x0009,0x0006 ;? ;ASCII 40 - 4f, 64 - 79 dw 0x0032,0x0049,0x0079,0x0041,0x003e ;@ dw 0x007e,0x0011,0x0011,0x0011,0x007e ;A dw 0x007f,0x0049,0x0049,0x0049,0x0036 ;B dw 0x003e,0x0041,0x0041,0x0041,0x0022 ;C dw 0x007f,0x0041,0x0041,0x0022,0x001c ;D dw 0x007f,0x0049,0x0049,0x0049,0x0041 ;E dw 0x007f,0x0009,0x0009,0x0009,0x0001 ;F dw 0x003e,0x0041,0x0049,0x0049,0x007a ;G dw 0x007f,0x0008,0x0008,0x0008,0x007f ;H dw 0x0000,0x0041,0x007f,0x0041,0x0000 ;I dw 0x0020,0x0041,0x0041,0x003f,0x0001 ;J dw 0x007f,0x0008,0x0014,0x0022,0x0041 ;K dw 0x007f,0x0040,0x0040,0x0040,0x0040 ;L dw 0x007f,0x0002,0x00c0,0x0002,0x007f ;M dw 0x007f,0x0004,0x0008,0x0010,0x007f ;N dw 0x007e,0x0041,0x0041,0x0041,0x007e ;O ;ASCII 50 - 5f, 80 - 95 dw 0x007f,0x0009,0x0009,0x0009,0x0006 ;P dw 0x003e,0x0041,0x0051,0x0021,0x005e ;Q dw 0x007f,0x0009,0x0019,0x0029,0x0046 ;R dw 0x0026,0x0049,0x0049,0x0049,0x0032 ;S dw 0x0001,0x0001,0x007f,0x0001,0x0001 ;T dw 0x003f,0x0040,0x0040,0x0040,0x003f ;U dw 0x001f,0x0020,0x0040,0x0020,0x001f ;V dw 0x003f,0x0040,0x0030,0x0040,0x003f ;W dw 0x0063,0x0014,0x0008,0x0014,0x0063 ;X dw 0x0007,0x0008,0x0070,0x0008,0x0007 ;Y dw 0x0061,0x0051,0x0049,0x0045,0x0043 ;Z dw 0x0000,0x007f,0x0041,0x0041,0x0000 ;[ dw 0x0000,0x0000,0x0000,0x0000,0x0000 ;spare (Yen) dw 0x0000,0x0041,0x0041,0x007f,0x0000 ;] dw 0x0004,0x0002,0x0001,0x0002,0x0004 ;^ dw 0x0001,0x0001,0x0001,0x0001,0x0001 ;_ ;ASCII 60 - 6f, 96 - 111 dw 0x0000,0x0001,0x0002,0x0004,0x0000 ;' dw 0x0020,0x0054,0x0054,0x0054,0x0078 ;a dw 0x007f,0x0048,0x0044,0x0044,0x0038 ;b dw 0x0038,0x0044,0x0044,0x0044,0x0020 ;c dw 0x0038,0x0044,0x0044,0x0048,0x007f ;d dw 0x0038,0x0054,0x0054,0x0054,0x0018 ;e dw 0x0008,0x007e,0x0009,0x0001,0x0002 ;f dw 0x000c,0x0052,0x0052,0x0052,0x003e ;g dw 0x007f,0x0008,0x0004,0x0004,0x0078 ;h dw 0x0000,0x0044,0x007d,0x0040,0x0000 ;i dw 0x0020,0x0040,0x0040,0x003d,0x0000 ;j dw 0x007f,0x0010,0x0028,0x0044,0x0000 ;k dw 0x0000,0x0041,0x007f,0x0040,0x0000 ;l dw 0x007c,0x0004,0x0018,0x0004,0x0078 ;m dw 0x007c,0x0008,0x0004,0x0004,0x0078 ;n dw 0x0038,0x0044,0x0044,0x0044,0x0038 ;o ;ASCII 70 - 7f, 112 - 127 dw 0x007c,0x0014,0x0014,0x0014,0x0008 ;p dw 0x0008,0x0014,0x0014,0x0018,0x007c ;q dw 0x007c,0x0008,0x0004,0x0004,0x0008 ;r dw 0x0048,0x0054,0x0054,0x0054,0x0020 ;s dw 0x0004,0x003f,0x0044,0x0040,0x0020 ;t dw 0x003c,0x0040,0x0040,0x0020,0x007c ;u dw 0x001c,0x0020,0x0040,0x0020,0x001c ;v dw 0x003c,0x0040,0x0030,0x0040,0x003c ;w dw 0x0044,0x0028,0x0001,0x0028,0x0044 ;x dw 0x000c,0x0050,0x0050,0x0050,0x003c ;y dw 0x0044,0x0064,0x0054,0x004c,0x0044 ;z dw 0x0000,0x0008,0x0036,0x0041,0x0000 ;{ dw 0x0000,0x0000,0x007f,0x0000,0x0000 ;| dw 0x0000,0x0041,0x0036,0x0008,0x0000 ;} dw 0x0008,0x0008,0x002a,0x001c,0x0008 ;-> dw 0x0008,0x001c,0x002a,0x0008,0x0008 ;<- ;alternates with descenders, ASCII 80 - 85, 128 - 134 dw 0x0000,0x0140,0x00c0,0x0000,0x0000 ;, ASCII 80, 128 dw 0x0038,0x0244,0x0244,0x0244,0x01f8 ;g ASCII 81, 129 dw 0x0100,0x0200,0x0204,0x01fd,0x0000 ;j ASCII 82, 130 dw 0x03f8,0x0044,0x0044,0x0044,0x0038 ;p ASCII 83, 131 dw 0x0038,0x0044,0x0144,0x02c4,0x0238 ;q ASCII 84, 132 dw 0x003c,0x0240,0x0240,0x0240,0x01fc ;y ASCII 85, 133 dw 0x0108,0x0244,0x0244,0x0224,0x01d8 ;z ASCII 86, 134 end ---------------------------------------------------------------------------------- ;Display '278 in large numbers across mid-screen boundary ;with 'Voltage' label underneath
Once again, the 4-part code here shows just one way to do this
;Start display at a known (x,y) co-ordinate 48,16 ;1st digit is contained entirely within controller 1's side bsf cs1 ;select left-hand controller usec bcf cs2 usec d278 movlw .48 ;48th column movwf column addlw y_base call write_c movlw .2 ;3rd page (starts @ line 16) addlw page_base call write_c movlw low(ch16) ;data base address (ie character "0") movwf tblptrl movlw high(ch16) movwf tblptrh clrf tblptru ;---------------- movlw .2 ;offset to first digit "2" mullw .20 ;at 20 bytes (10 words) of data per character movfw prodl ;add character offset to base address addwf tblptrl movfw prodh addwfc tblptrh ;---------------- ;top half of character movlw .10 ;data counter movwf temp0 top_c1 tblrd*+ ;fetch low byte movfw tablat call write_d ;display it tblrd*+ ;fetch, ignore, high byte decfsz temp0 ;loop bra top_c1 ;bottom half of character movfw column ;reset to coumn 48 addlw y_base call write_c movlw .3 addlw page_base ;next page, page 4 (starts @ line 24) call write_c ;fetch same data words as before but use the high bytes movlw low(ch16) ;data base address (ie character 0) movwf tblptrl movlw high(ch16) movwf tblptrh clrf tblptru movlw .2 ;first digit mullw .20 ;20 bytes of data per character movfw prodl ;add character offset addwf tblptrl ;to base address movfw prodh addwfc tblptrh movlw .10 ;data counter movwf temp0 bot_c1 tblrd*+ ;fetch low byte, ignore tblrd*+ movfw tablat call write_d ;display it decfsz temp0 ;loop bra bot_c1 ;---------------- movlw 0x00 ;two-pixel space before next digit call write_d ;column = 58 call write_d ; = 59 ;column for start of second character = 60 ;go back up a page for top half of next character movlw .2 ;3rd page (starts @ line 16) addlw page_base call write_c movlw low(ch16) ;data base address (ie character 0) movwf tblptrl movlw high(ch16) movwf tblptrh clrf tblptru ;---------------- movlw .7 ;offset to data for second digit "7" mullw .20 ;at 20 bytes (10 words) of data per character movfw prodl ;add character offset to base address addwf tblptrl movfw prodh addwfc tblptrh ;---------------- ;top half of character clrf temp0 ;data counter top_c2 tblrd*+ ;fetch low byte movfw tablat call write_d ;display it tblrd*+ ;fetch, ignore, high byte incf temp0 ;if temp0 = 10, top is finished movlw .10 cpfslt temp0 ;else not 10, check for boundary bra c2_low ;if column is < 64 then set cs1 ;if column is = 64 then set cs2 movlw .4 cpfseq temp0 ;start column (60) + 4 data = 64 bra top_c2 bcf cs1 ;switch to right-hand controller usec bsf cs2 usec movlw .0 ;and column 0 addlw y_base call write_c movlw .2 addlw page_base ;maintain page number call write_c bra top_c2 ;loop ;bottom half of character c2_low bsf cs1 ;back to left-hand controller usec bcf cs2 usec movlw .60 ;reset to column 60 addlw y_base call write_c movlw .3 addlw page_base ;down a page (starts @ line 24) call write_c ;fetch same data words as before but use the high bytes c2_datl movlw low(ch16) ;data base address (ie character 0) movwf tblptrl movlw high(ch16) movwf tblptrh clrf tblptru movlw .7 ;second digit, "7" mullw .20 ;20 bytes of data per character movfw prodl ;add character offset addwf tblptrl ;to base address movfw prodh addwfc tblptrh clrf temp0 ;data counter bot_c2 tblrd*+ ;fetch low byte, ignore tblrd*+ ;fetch high byte movfw tablat call write_d ;display it incf temp0 ;if temp0 = 10, bottom is finished movlw .10 cpfslt temp0 ;not 10, check for boundary bra digit3 ;else finished ;if column is < 64 then set cs1 ;if column is > 63 then set cs2 movlw .4 cpfseq temp0 bra bot_c2 bcf cs1 ;switch to right-hand controller usec bsf cs2 usec movlw .0 ;and column 0 addlw y_base call write_c movlw .3 addlw page_base call write_c bra bot_c2 ;---------------- ;3rd digit is contained entirely within controller 2's side digit3 movlw 0x00 ;two-pixel space before next digit call write_d call write_d movlw .2 ;3rd page (starts @ line 16) addlw page_base call write_c movlw low(ch16) ;data base address (ie character 0) movwf tblptrl movlw high(ch16) movwf tblptrh clrf tblptru ;---------------- movlw .8 ;offset to third digit "8" mullw .20 ;at 20 bytes (10 words) of data per character movfw prodl ;add character offset to base address addwf tblptrl movfw prodh addwfc tblptrh ;---------------- ;top half of character movlw .10 ;data counter movwf temp0 top_c3 tblrd*+ ;fetch low byte movfw tablat call write_d ;display it tblrd*+ ;fetch, ignore, high byte decfsz temp0 ;loop bra top_c3 ;bottom half of character movlw .8 ;reset to column 8 addlw y_base call write_c movlw .3 addlw page_base ;next page (starts @ line 24) call write_c ;fetch same data words as before but use the high bytes movlw low(ch16) ;data base address (ie character 0) movwf tblptrl movlw high(ch16) movwf tblptrh clrf tblptru movlw .8 ;third digit mullw .20 ;20 bytes of data per character movfw prodl ;add character offset addwf tblptrl ;to base address movfw prodh addwfc tblptrh movlw .10 ;data counter movwf temp0 bot_c3 tblrd*+ ;fetch low byte, ignore tblrd*+ movfw tablat call write_d ;display it decfsz temp0 ;loop bra bot_c3 call v_label ;the routine where you get to figure ;out how to break a character across ;the mid-screen boundary