6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Apr 28, 2024 2:57 am

All times are UTC




Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: Sun May 31, 2020 2:07 pm 
Offline

Joined: Mon Apr 27, 2020 6:54 am
Posts: 7
Hello,

how can I read out vast amount of texts (hundreds of lines) and display them on a 20x4 display?
What I did so far is reading up to 256 characters from the space indicated with .BYTE or .ASCIIZ and displaying it, even with scrolling the lines from bottom to top on the 20x4.
Now I want to address much more text and I fear, I miss even how to ask the right questions regarding how to do it. Because reading from .BYTE is easy to do with just counting until 255 and handing the characters over to the display. But how to proceed for much more text stored in the program? How can i read this out properly?

Thank you for some suggestions.


Top
 Profile  
Reply with quote  
PostPosted: Sun May 31, 2020 4:11 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1927
Location: Sacramento, CA, USA
You're going to want to come up with a plan to allow the user to interactively pause and/or scroll the display up and down. To access the text, I would use indirect addressing with a zero-page pointer, but I don't have time to provide a specific example ... my broken down house desperately needs my help right now ...

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sun May 31, 2020 4:20 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
It is fairly easy to handle more than 256 characters from a buffer. Consider:

Code:
    lda   #<Buffer
    sta   ZPPtr
    lda   #>Buffer
    sta   ZPPtr+1
    ldy   #0

Loop:
    lda   (ZPPtr),Y
    beq   Done
    jsr   Output

    iny
    bne   Loop

    inc   ZPPtr+1

    jmp   Loop

Done:


Top
 Profile  
Reply with quote  
PostPosted: Tue Jun 02, 2020 1:09 pm 
Offline

Joined: Fri Jan 25, 2019 2:29 pm
Posts: 191
Location: Madrid, Spain
Hi,

I've implemented, pretty much exactly what you're looking for, on my first SBC. You can find the current firmware here although it's a very preliminary version.

This is what I've done. I've assigned a nice chunk of RAM ($7000-$7FFF) as "screen memory". I then use two bytes in ZP as a pointer to the beggining of the portion I want to show on the display. On startup, $10.$11 points to the beggining of my screen:

Code:
// Top left corner of screen at startup
.const SCREEN_BASE      = $7000
// 10 11. Pointer to top left on Screen
.const SCREEN_POINTER   = $10

         lda #<SCREEN_BASE
         sta SCREEN_POINTER
         sta SCREEN_CURSOR_POINTER
         lda #>SCREEN_BASE
         sta SCREEN_POINTER+1    // Set up base window
         sta SCREEN_CURSOR_POINTER+1


I have a routine that moves the LCD cursor to the first location, then sends the first 80 characters (starting at (SCREEN_POINTER) to the LCD. Keep in mind the lines are "out of order" on the LCD (First, then third, second, and fourth"

Code:
scrRefresh:
         // Moves screen window to LCD
         pha
         phy

         lda #$80
         jsr lcdInstr
         lda #$00
         jsr lcdInstr       // Set cursor to 0x0

         ldy #$00
dl1:      lda ($10),y
         jsr lcdChar
         iny
         cpy #$14
         bne dl1

         ldy #$28
dl3:      lda ($10),y
         jsr lcdChar
         iny
         cpy #$3C
         bne dl3

         ldy #$14
dl2:      lda ($10),y
         jsr lcdChar
         iny
         cpy #$28
         bne dl2

         ldy #$3C
dl4:      lda ($10),Y
         jsr lcdChar
         iny
         cpy #$50
         bne dl4

         ply
         pla
         rts


This routine is called by my interrupt service routine. Given the slow refresh rate of the LCD display, 5 times per second is more than enough. BTW, lcdChar is a routing that sends the character on A to the LCD.

Finally, to move the window, I have two routines to scroll up or down a line. This is simply done by adding/substracting 20 from the current SCREEN_POINTER. Some additional code is needed to check for the boundaries of my screen memory.

Code:
scrScrollUp:
         pha
   
         lda SCREEN_POINTER         
         sec
         sbc #$14             // Substract 20 from screen_pointer
         sta SCREEN_POINTER
         bcs scrScrollUpEnd      // Carry Clear, no overflow
                           // overflow
         dec SCREEN_POINTER+1   // DEC HI BYTE
         lda SCREEN_POINTER+1   
         cmp #$6f
         bne scrScrollUpEnd                  // Not at screen end, so end routine
         lda #<SCREEN_BASE
         sta SCREEN_POINTER
         lda #>SCREEN_BASE
         sta SCREEN_POINTER+1    // Reset screen_pointer to $7000
scrScrollUpEnd:
         pla
         rts

scrScrollDown:
         pha

         clc
         lda SCREEN_POINTER
         adc #$14            // Add 20 to screen pointer
         sta SCREEN_POINTER

         bcc scrScrollDownEnd       // Carry Clear, so overflow
                           // overflow
         inc SCREEN_POINTER+1   // INC HI BYTE
         lda SCREEN_POINTER+1   
         cmp #$7f
         bne scrScrollDownEnd                  // Not at the screen bottom, end
         lda #$EC
         sta SCREEN_POINTER
         lda #$7E
         sta SCREEN_POINTER+1
scrScrollDownEnd:
         pla
         rts


As I said, is very preliminary, and it is been a very long time since I did anything in assembler... there is a big potential for improvements for sure. But I can say it does work.

Hope it helps.

Cheers!


Top
 Profile  
Reply with quote  
PostPosted: Tue Jun 02, 2020 9:11 pm 
Offline

Joined: Mon Apr 27, 2020 6:54 am
Posts: 7
Thank you all so much so far! Since I'm really new in programming in general and in assembly language especially, I tried to take any hint very serious.

Thanks to @barrm95838, I started to read quite a bit on all addressing modes for the 65C02 and especially on indirect addressing, memory structure and so on.

Thanks to you, @BillG, I was then able to apply your code and learn with it. I made it to read out from the data saved in the ROM for more than 255 characters and in my case to store them in the RAM at a certain address.
I am following an approach to enter a routine to display four lines (4x20 characters) at once and then to store the "next" 4 lines (which are the previous ones minus 60 characters) at that buffer in the RAM.
You can imagine now, where i failed: Exactly at that point where the 255 counter needs to subtract the "60 characters" from the pinter (right term here?) when i reached already 255 lines (and the ZPPtr+1 needs to be subtracted as well and so on).

@daniMolina: Thank you for your post! I will try your code as well. I have some questions: Do you store the long text you are displaying at the address starting with $7000? Where is the text located first? Because I burn all the text first in the EEPROM and then try to shift it to an address in the RAM from where I read it in for the display. Where in your code is this transfer from ROM to RAM (starting with $7000, I assume)? I'm pretty sure I am missing something. Thanks for some more hints!

Here is my code example. What works perfectly is displaying the first 255 characters, each 4 lines are perfectly scrolling ... until I reach the 255 threshold and it doesn't to subtract properly (see below in the print "ROWS" routine: SBC #60 and so on):

Code:

ZPPTR = $02
 
     .ORG $8000

;   SED         ;  not sure if I need some of these Codes, tried, but didn't change much
;   SEC

[ Here is the initialization routine for the display ]

   LDA #<TEXT
   STA ZPPTR
   LDA #>TEXT
   STA ZPPTR+1
   LDY #0
LP:   
   LDA (ZPPTR),Y
   BEQ DONE
   JSR MEM
   INY
   BNE LP
   INC ZPPTR+1
   JMP LP

MEM:
   STA $0100, X   ; storing 80 characters from location $0100 on
   INX
   CPX #80
   BEQ ROWS
   RTS

DONE:
   LDA #$01     ; clear display
   JSR IS

DONE1:
   JMP DONE1

; --- SUBROUTINE FOR DISPLAYING 80 CHARACTERS ---

ROWS:
   PHY
   LDX #0
   LDY #0
   STY $00      
ZIS:
   LDY $00      
   LDA LCD, Y   
   JSR INSTRUCTION         
   LDY #0
ZPR:
   LDA $0100, X   
   JSR PRINT
   INX
   INY
   CPY #20
   BNE ZPR
   LDY $00
   INY
   STY $00
   CPY #4
   BNE ZIS
   LDX #0
   PLY
   TYA
   SBC #60
   TAY
   RTS

INSTRUCTION: 
[Here is a subroutine for LCD instructions]

PRINT:
[Here is a subroutine for LCD printing characters]
   

LCD:.BYTE $80, $C0, $94, $D4     ; Starting addresses for the 4 LCD Display lines

TEXT:
   .BYTE "[...very long text ...]"



Attachments:
65C02.jpg
65C02.jpg [ 124.97 KiB | Viewed 1126 times ]
Top
 Profile  
Reply with quote  
PostPosted: Tue Jun 02, 2020 10:42 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
manhattanmoments wrote:
Here is my code example. What works perfectly is displaying the first 255 characters, each 4 lines are perfectly scrolling ... until I reach the 255 threshold and it doesn't to subtract properly (see below in the print "ROWS" routine: SBC #60 and so on):


First, a comment, then I will get to the cause of your problem.

The SBC instruction uses the carry flag to indicate whether there is a borrow from a previous subtraction; carry set means no borrow.

Usually, you will see the sequence

Code:
    sec
    sbc


It was not a problem in your case because the code above

Code:
   CPY #4
   BNE ZIS


was leaving carry set. Advanced programmers sometimes take advantage of these opportunities to save bytes and cycles, but document them well because the code is not as obvious.

Anyway...

The code

Code:
   INY
   BNE LP
   INC ZPPTR+1
   JMP LP


steps from one 256-byte page of text to the next.

When you are trying to step back by subtracting three lines from Y, you are not reversing the process when you hit the beginning of a page.

Code:
   SEC
   SBC #60
   BCS Same

   DEC ZPPTR+1   ; Go to previous page

Same:
   TAY


Top
 Profile  
Reply with quote  
PostPosted: Tue Jun 02, 2020 11:29 pm 
Offline

Joined: Mon Apr 27, 2020 6:54 am
Posts: 7
BillG wrote:
First, a comment, then I will get to the cause of your problem.

The SBC instruction uses the carry flag to indicate whether there is a borrow from a previous subtraction; carry set means no borrow.

Usually, you will see the sequence

Code:
    sec
    sbc


It was not a problem in your case because the code above

Code:
   CPY #4
   BNE ZIS


was leaving carry set. Advanced programmers sometimes take advantage of these opportunities to save bytes and cycles, but document them well because the code is not as obvious.


Thank you! That is very helpful to understand. I will integrate for now SEC and SBC and comment it.


BillG wrote:
The code

Code:
   INY
   BNE LP
   INC ZPPTR+1
   JMP LP


steps from one 256-byte page of text to the next.

When you are trying to step back by subtracting three lines from Y, you are not reversing the process when you hit the beginning of a page.

Code:
   SEC
   SBC #60
   BCS Same

   DEC ZPPTR+1   ; Go to previous page

Same:
   TAY


Thanks a lot for this information! It solved the problem, everything works fine now!

So the carry flag is set, when after the subtraction of 60 the counter does not need to cross the 255 border again. Correct?

Made my day!


Top
 Profile  
Reply with quote  
PostPosted: Wed Jun 03, 2020 1:41 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
Just a few tips on efficiency [Edit: see the correction comments further down though]:

What is this part supposed to do?
Code:
   LDY $00
   INY
   STY $00
   CPY #4
   BNE ZIS

It loads Y with 0, then immediately increments it to 1; so why not just load 1 into Y in the first place and skip the increment. Next, it is being compared to 4, and of course it will never match (because it's always 1 at that point).

Also this:
Code:
   PLY
   TYA
   SBC #60
   TAY

Why not just PLA, SBC; and then if you want the value in Y you can do the TAY.

Then there's this:
Code:
ROWS:
   PHY
   LDX #0
   LDY #0
   STY $00     
ZIS:
   LDY $00   

When it arrives at the STY, both X and Y have 0 in them, and Y gets loaded with 0 again right below; so why not just do STX instead, and skip the first LDY #0. In the JSR INSTRUCTION right after that, does "INSTRUCTION" modify Y? If not, the LDY #0 immediately following the JSR is redundant.

There are more programming tips in that section of the 6502 primer, at http://wilsonminesco.com/6502primer/PgmTips.html .

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Wed Jun 03, 2020 1:48 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
Hmm, there may be some confusing between load-immediate and load-zeropage in the above analysis. But some of the points remain valid.


Top
 Profile  
Reply with quote  
PostPosted: Wed Jun 03, 2020 1:50 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
Chromatix wrote:
Hmm, there may be some confusing between load-immediate and load-zeropage in the above analysis. But some of the points remain valid.

Oops. You're right. My mistake comes because numbers as operands should virtually never be addresses! Addresses need to be given descriptive names. Besides the readability factor, if you ever need to change it, you only change it in one place, and then every place the assembler sees the name, it substitutes-in the right address.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Wed Jun 03, 2020 8:24 am 
Offline

Joined: Fri Jan 25, 2019 2:29 pm
Posts: 191
Location: Madrid, Spain
manhattanmoments wrote:
@daniMolina: Thank you for your post! I will try your code as well. I have some questions: Do you store the long text you are displaying at the address starting with $7000? Where is the text located first? Because I burn all the text first in the EEPROM and then try to shift it to an address in the RAM from where I read it in for the display. Where in your code is this transfer from ROM to RAM (starting with $7000, I assume)? I'm pretty sure I am missing something. Thanks for some more hints!
รง

It's stored nowhere. I have a "charPrint" routine that moves a character to (SCREEN_CURSOR_POINTER) which is $7000 on startup. After each char is printed, it increases the pointer by 1. On top of this I've built a "stringPrint", "decPrint", and "hexPrint" routines.

My idea was not to display a long text, but to have the hability to print to screen, and then scrollback to see what's been going on, so there's no initial big text stored anywhere.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 16 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: