6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu Jul 04, 2024 4:30 pm

All times are UTC




Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Dealing with Data
PostPosted: Thu Jul 05, 2007 5:43 pm 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
Hi everyone...

Just subscribed to this 6502 board, so I thought I'd try it out!!

What is the best way to deal with data in assembly? What I mean by that is that I have 2KB of Font data in the EEPROM of my 6502 homebuilt computer. Each 8 bytes of data is one character. When I receive an ASCII code from my keyboard, say the letter "A" which is 65, how can I jump through the data, to the proper byte, in order to send it to my display.

Am I making any sense to anyone?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 05, 2007 6:37 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8462
Location: Southern California
Quote:
Each 8 bytes of data is one character.

So is this 8 bytes of raster graphics information for example for how the display should form the character? If so, there are quite a few ways it could be done. Can you tell us more about how the display needs to be fed? What kind of display is it? Are there already display routines in place?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 05, 2007 7:14 pm 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
The 8 bytes is already in the form required to send to my LCD display. I am currently writing the display routine.

Each byte contains 8 bits from bottom to top of the character, and then the next byte fills the next 8 bits from bottom to top.... and so on until all 8 bytes are used.

My keybaord routine sends me the ascii code for each key pressed, now I need a way to fetch the proper 8 bytes of data that correspond to that ascii code... i.e. if you press 'A' on the keyboard, the value passed to the Accumulator is 65, so now I some how have to wade through my 2048 bytes of data until I reach A, which is 521 bytes into my data. I'm unsure as to how to figure out which memory location to fetch the data from.

If I could get each byte of data to the accumulator when needed, I have no problem getting it to my LCD display.

Is that any clearer? FYI, I put each byte in my assembly code using the .byte directive.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 05, 2007 7:37 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8462
Location: Southern California
So it's a graphics LCD without a built-in character generator? If the dots are square or nearly square, you'll get a more pleasing aspect ratio for each letter and more letters on a line if you make them the narrower 6x8 (with one column usually being the blank one for space between letters) instead of 8x8. Anyway, do the first 8 bytes of the display data table correspond to the first character, perhaps $20 (space), the next 8 bytes to $21 ("!"), the next 8 bytes to $22 (the double quote mark), etc?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 05, 2007 7:46 pm 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
Yes, exactly.. it's a graphical LCD, and the data is in sequence with ASCII data as you said.... $20, $21, $22 = space, ! , "....... $41, $42, $43 = A, B, C..... etc.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 05, 2007 10:37 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8462
Location: Southern California
So suppose you call a routine initially with the ASCII value in the accumulator, and the name of your table is DISP_DATA_TBL . The table will definitely span more than one page, especially if you go from $20 to $7F, so a simple 8-bit index added to the table's beginning address won't be enough if the eight bytes for each character are kept together. One alternative would be to have a table of first bytes, a table of second bytes, and so on, totaling eight tables, which could be indexed with the same 8-bit index, producing shorter code.

You could have an initial routine CHR_INIT that gets the base address of the 8 bytes of data for the particular character's graphics data, and then have a routine GET_COL that looks up the byte to give to the LCD and increments the byte number for the next iteration. You'd call the latter once for every iteration of feeding the display, which means eight times for one character.

Make TBL_OFFSET and CHR_ADR below to be a two-byte ZP variable. Since they're not needed at the same time, they can be the same two bytes with two diferent names for clarity as to what they're doing. Assembler directives may vary, depending on what assembler you're using.
Code:
TBL_OFFSET:
CHR_ADR:       DFS  2         ; These two variables can use the same ZP space.
BYTE_NUMBER:   DFS  1         ; This keeps track of what column to load next.


CHR_INIT: STZ  TBL_OFFSET+1   ; Init the high byte to be 0.
          ASL  A              ; Shift the chr number left 3 times to multiply
          ASL  A              ; by 8.  The 1st one will not shift the high byte out,
          ROL  TBL_OFFSET+1   ; but the 2nd and 3rd shifts might, so that bit must be
          ASL  A              ; rotated into the high byte.
          ROL  TBL_OFFSET+1   ; Now the low byte of the table offset is in A.
                              ; Add this offset to the address of the table itself
          CLC                     ; to get the starting address of the 8 bytes of
          ADC  #<DISP_DATA_TABLE  ; interest.  Add the low byte of the table
          STA  CHR_ADR            ; address and store it.
          LDA  TBL_OFFSET+1       ; Get the high byte of the offset,
          ADC  #>DISP_DATA_TABLE  ; and add it to the high byte of the table,
          STA  CHR_ADR+1          ; and store it.  Now the address of the 8 bytes is
                                  ; complete.
          STZ  BYTE_NUMBER    ; The index into the set of 8 bytes will start with 0.
          RTS
 ;--------------------
GET_COL:  LDY  BYTE_NUMBER
          LDA  (CHR_ADR),Y    ; Get the required byte in A,
          INC  BYTE_NUMBER    ; and get ready to fetch the next byte.
          RTS
 ;--------------------

If you wanted to do it on the 65816, it would be simpler. ;)


Last edited by GARTHWILSON on Fri Jul 06, 2007 5:47 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 05, 2007 11:55 pm 
Offline

Joined: Tue Nov 18, 2003 8:41 pm
Posts: 250
This is sort of redundant by now but..

welcome DonnaD

You're still not quite giving enough information
I'll assume that that bytes for individual characters
are in ascending order in the table.
ie location 521 is the first byte of "A" 522 is the
second 523 the third etc.

(a quibble here, I'd start counting with 0 and put
the first byte of "A" at 520)

Also assuming that the character codes correspond
to table locations so that they can be used as
indexes to the corrresponding block in the table
(ie that's how I interpret what you've said)

Let me apologize in advance if I'm being needlessly
verbose or pedantic (I'm guessing it will be useful
or you wouldn't be asking the question in the first
place)

Also for doing goofy things like counting from
0 instead of 1 and including instructions in the
code that really aren't needed ;)

I guess the straight forward way would be to form
a base address for the character data in the table
by multipying the character code by 8 and adding
the value of the start of the table to that to get
the start of the block for that character, then count
through the individual bytes with Y register

The Y register is used with the indirect indexing
mode of addressing, which is precisely for this
sort of situation.
You supply a zero page location, the cpu retrieves
the two bytes of a 16 bit address from it,
lo byte first, ie the zp location you specify in
the instruction contains the lo byte and the next
location contains the high byte, it then adds the
the value in the Y register to that 16 bit value
and uses the result as the address for the data

So, conceptually, you'd mutiply your character by 8
add the base address of the table to that put it
in a zeropage location and point your indirect
addressed instuction at that, manipulating the
Y register to get the individual bytes

I say conceptually 'cause (I think) that's the
straight forward way to think of it, but for
practical reasons you'd do it in a slightly different
order, multply by 8 as you store it to zero page then
add the base-of-the-table address.
Since you want a 16 bit address and the cpu only deals
with 8 bits of data at a time (the address is data
while you're multiplying and adding) you need temporary
storage for the piece of the address you're not working on
at the moment.
Might as well be your zero page pointer location and you
can do some of it in place (shifting, specifically) with
out moving stuff in and out of the accumulator.

You multiply by 8 by shifting.
we're working in binary so shifting one bit position
has the effect of multiplying or dividing by 2
three shifts to the left to multiply by 8

Code:
 ; Assuming the character code is in the accumulator
 ; And we've chosen a start address for the table
 ; that's a mutiple of 256 so that the lo byte is 0
 ; so we only have to add the hi byte.
 ; pointerhi and pointerlo are the zp locations
 ; of the pointer
 ; tblebaseaddresslo and tablebaseaddresshi
 ; are the lo byte and the hi byte of the address
 ; of first entry of the character data table


 ldy #$00             ; clear the hi byte of the zp pointer
 sty pointerhi        ; we'll deal with the zp lo byte later
                      ; for now it's, in the accumulator


 asl                  ; acc x 2,  the lo byte of the offset
 rol pointerhi        ;now the hi byte x 2 and shift the previous
                      ; hi bit of the lo byte into the lo bit
                      ; of the hi byte of the pointer
 asl                  ; do it again to get character x 2 x 2
 rol pointerhi
 asl                  ; again for character x 2 x 2 x 2 = character x 8
 rol pointerhi

 ; we've got our character offset now we can add our table base
 ; address to it, the lo byte of the offset is still in the accumulator

 ;clc                   "normally" we'd insert a clc here
                      ; in preparation for addition, but
                      ; we should have just rol'd a 0 into
                      ; the C flag so it's already cleared

 ;adc tblbaseaddresslo  assuming we chose a starting address
                      ; for the table such that the lo byte of
                      ; the table address is 0 we can skip adding
                      ; the lo byte, adding 0 wouldn't change it
                      ; so we may not need this instruction

 sta pointerlo        ; move it from the acc to the lo byte of the zp pointer
 lda pointerhi        ; get the hi byte of the pointer
 adc tblbaseaddresshi ; and add the hi byte of the table base address
 sta pointerhi        ; put the result back in tho the pointer
 

 ; the zp pointer should now contain the address of the start of
 ; data for the individual character


 ;ldy #$00              here we'd "normally" intialze Y.
                      ; the first byte is the zeroth byte,
                      ; I start my counting at 0 not 1
                      ; but we don't actually need this instruction
                      ; here, Y should still be zero

loop
 lda (pointerlo),y    ;  here's your indirect indexed adressing
 sta lcd
 iny

 cpy #$08             ; the last byte of character data is the 7th,
 bne loop             ; did we just pass it? if not go back and
                      ; get another byte of character data



Garth Wilson left out a rol but you did specify a 2k table
with 8 bytes for each entry, 256 characters, so I left it in.


That's (I think) the straight forward way
but there's lots of room to fiddle with details
particularly if you're willing to reorder your table

You could treat the character code in the accumulator as the hi byte and
shift it right in to the lo byte (it may or may not save you anything
it's just another posibility to consider)

You could put the character data in reverse order and so that the first
is in the 7th location, second in the 6th location, etc so that you decrement
the Y register and quit when it reaches 0 (the decrement instruction sets
the Z flag) that could save you doing the cpy #$08 in the loop (but it
might not save you anything over all)

You could split the tabel in to individual character byte positions
ie a table of all of the first character bytes, another table of all the second
bytes another of all the third etc
and index them directly with the character code and save the mutiplying and adding

Code:
 tya
 lda table1,y
 sta lcd
 lda table2,y
 sta lcd
 .
 .


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jul 06, 2007 12:19 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8462
Location: Southern California
bogax has some good considerations. I will just offer a correction on one thing:
Quote:
lda (pointerlo),y

There is no LDA (ind),Y instruction. There's LDA (ind),X, and LDA (ind,Y), but no LDA (ind),Y. The confusion between preindexing and postindexing continues for quite awhile after you get started. For postindexing, you have to use X. Preindexing with Y is for when you want to add the index value to the operand before getting the address to look up what you finally need. Postindexing is for when you look up the base address as of a table, and after you have the address of the table, you index into the table.

EDIT: After discussion below, I must say disregard this post. I'll leave it in as a place keeper though.


Last edited by GARTHWILSON on Fri Jul 06, 2007 5:48 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jul 06, 2007 2:51 am 
Offline

Joined: Tue Nov 18, 2003 8:41 pm
Posts: 250
GARTHWILSON wrote:
The confusion between preindexing and postindexing continues for quite awhile after you get started.


apparently ;)


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jul 06, 2007 3:10 am 
Offline

Joined: Tue Nov 18, 2003 8:41 pm
Posts: 250
bogax wrote:
You could split the tabel in to individual character byte positions
ie a table of all of the first character bytes, another table of all the second
bytes another of all the third etc
and index them directly with the character code and save the mutiplying and adding

Code:
 tya
 lda table1,y
 sta lcd
 lda table2,y
 sta lcd
 .
 .


Ack!

That should be

Code:
 tay                    <<< not  tya
 lda table1,y
 sta lcd
 lda table2,y
 sta lcd
 .
 .


assuming we start with the character code in a


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jul 06, 2007 3:52 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
GARTHWILSON wrote:
Quote:
lda (pointerlo),y

There is no LDA (ind),Y instruction. There's LDA (ind),X, and LDA (ind,Y), but no LDA (ind),Y.

Uh, bogax had it right.

$A1 is the opcode for LDA (ind,X)
$B1 is the opcode for LDA (ind),Y

One way to remember it is that the ,X comes before the right parenthesis, and the ,Y comes after the right parenthesis, just like X comes before Y in the alphabet and Y comes after X in the alphabet.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jul 06, 2007 4:18 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8462
Location: Southern California
Yep, you caught me getting confused in the notation myself. Too hurried. I copied from an instruction chart I knew had an error in it in a book and I even had it marked, and still copied the error. I apologize for wasting people's time. I need to get back to 65xx assembly programming and refresh that stuff. I replaced the X with Y in the routine in my 3rd post above.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jul 06, 2007 1:55 pm 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
Wow... thanks....

That's enough to keep me going for a while... I'll let you know how I make out.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jul 06, 2007 9:01 pm 
Offline

Joined: Tue Nov 18, 2003 8:41 pm
Posts: 250
I wasn't going to confuse the issue, but I think
I'll expand slightly on why you might want to be
"counting from 0 instead of 1"

Since you're working in binary and you're dealing with
things that are powers of two, 8 bytes per character
256 characters, and (therefore) a 2k table if you 'count
from 0' the table location the character and the byte
can be independent of each other

You can compose the pointer simply by stringing things
together with out addition. You could OR them together
for example.

You might think of it as composing a multi digit number
where the first digit has 8 possible values the second
256 and the third 32 (64k address range/2K)
The values are all integral mutiples of each other,
so you can work with integers and not have to deal with
messy fractions. You just have to stay with integers and
get the right digits in the right order, but one digit does
not depend on another.

In this case, you could do away with the addition operation
altogether.


Code:
 ; this assumes that the table base address is an integral
 ; multiple of 2k

 ldy #$xx       ; this will be the table base address/(8*256)
                ; that is, the high byte shifted right 3 bits
                ; so it'll come out right after we've shifted
                ; three of the character bits into it
 sta pointerhi

 asl
 rol pointerhi
 asl
 rol pointerhi
 asl
 rol pointerhi

 sta pointerlo

 ;we've dispensed with the addition

 ldy #$00       ; but now we have to intialize Y


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Jul 07, 2007 9:13 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
Why not consider alternative data orders such as keeping all bit patterns for the rows of the characters in 8 different tables indexed by character code. The use simple indexed addressing eg.
Code:
; Assume character code in X preserved during JSRs

JSR InitialiseGraphicsWrite
LDA ROW1,X
JSR OutputGraphicsData
LDA ROW2,X
JSR OutputGraphicsData
LDA ROW3,X
JSR OutputGraphicsData
LDA ROW4,X
JSR OutputGraphicsData
LDA ROW5,X
JSR OutputGraphicsData
LDA ROW6,X
JSR OutputGraphicsData
LDA ROW7,X
JSR OutputGraphicsData
LDA ROW8,X
JSR OutputGraphicsData

...

ROW1: .BYTE $00, $01, ... ; 256 bytes of data for each row
ROW2: .BYTE $00, $01, ...
ROW3: .BYTE $00, $01, ...
ROW4: .BYTE $00, $01, ...
ROW5: .BYTE $00, $01, ...
ROW6: .BYTE $00, $01, ...
ROW7: .BYTE $00, $01, ...
ROW8: .BYTE $00, $01, ...

Arranging the graphics data in the row groups is a pain by hand so write a simple program to generate the data statements and cut/paste them into your source.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: