LCD menu for 16F628A in Asm

takao21203

Joined Apr 28, 2012
3,702
You are missing the point. They have the same offset if the offset is only 8 bits. You need to maintain and calculate a 16 bit table address and maintain PCLATH as well. Here's something I whipped up for you to give you the idea. It assembles and works in MPSIM. The main point is that you calculate a FULL ROM address and jam it onto the program counter (which hopefully points to a retlw!) You should take steps to see that your calculated address is valid etc etc...

Rich (BB code):
;************************** TABLE 16 EXAMPLE **************************
    ; Untested but you get the idea - not exactly optimized either..

    INCLUDE "p16F648a.inc"

    cblock (20h)
        TablAddrL: 1    ; declare 2 byte table address
        TablAddrH: 1
        savePCLATH:1    ; saves PCLATH during table ops
        TargetBuf: .32    ; somewhere to put it
    endc


    ORG 0

    ;---------------------------
    ; typical code flow. Could automate this setup with a macro..
    ;...
    movlw    TargetBuf            ; Set target buffer
    movwf    FSR                    ; FSR-> where data goes
    bcf        STATUS,IRP

    movlw    low (Text30)        ; set ROM address of data to get
    movwf    TablAddrL
    movlw    high (Text30)    
    movwf    TablAddrH

    call    GetData_FSR            ; gets string from ROM to *FSR
    ;*
    goto    0                    

    ;-------------------------------------------
    ; GetData_FSR Copies RETLW xx ROM data from
    ; TablAddrH:L to *IRP:FSR 
GetData_FSR:
    movf    PCLATH,W            ; save PCLATH of current page
    movwf    savePCLATH

    call    _GetLoop            ; fetches data from TablPtr until \0

    movf    savePCLATH,W        ; restore PCLATH 
    movwf    PCLATH
    return

    ;----------------------------------------------
    ; Gets data from ROM at *TablAddrH:L ->*FSR until
    ; data is 00h. Doesn't store 00h

_GetLoop:
    call    lookup16_W            ; fetch one byte via RETLW xx
    iorlw    0                    ; set flags on W (look for \0 to terminate string)
    skpnz
    return                        ; done on data==Z

    movwf    INDF                ; else, got one byte, save it at *FSR
    incf    FSR,F                ; FSR++

    incf    TablAddrL,F            ; bump 'table' pointer to next RETLW xx
    skpnz
    incf    TablAddrH,F
    goto    _GetLoop

    ;-------------------------------------------------
    ; Jams TablAddrH:L onto PCLATH:PCL to force a jump

lookup16_W:
    movf    TablAddrH,W            ; forced goto to TablAddrH:L
    movwf    PCLATH
    movf    TablAddrL,W            ; returns through RETLW xx
    movwf    PCL



    ORG    600h        ; your table, page 1 (could be anywhere)
TableStart:
Text1    dt    "This is text 1",0
Text2    dt    "This is text 2",0
    ;.. .blah blah

    ; Table runs past 600h into page 700h.. but ORG is not needed

    ; blah blah
    ORG 700h
Text30    dt    "Text 30 is in 7xx",0

    END
I must say that I actually hate all this.

This does not mean your particular effort of work, or project.
This does not mean the actual forum contribution.
All this is appreciate and might be very reasonable to do.

I am saying, it is not appreciate for me, and I strongly don't like it.

I have done complex LCD menu in assembler. At first multilevel tables inside program FLASH, later on, computing indexing tables, storing the data into EEPROM, reading it back (with a different routine), and even removing it from program FLASH. It is stretching over many pages.

Why would you want to code 16bit FLASH table access using a lenghty construct as above?

When you can use this:

Rich (BB code):
char* get_font_ptr(char* font)
{unsigned int addr;
 addr=*(font+1);addr<<=8;addr|=*(font);
 return(((const char*)(addr)));
}
Maybe yes go through all this, create index tables inside EEPROM, write more code to parse the LCD menu from EEPROM, and see with how many pages of source you will end up.

This is all completely the free choice of the OP.
I am just saying I have coded stuff like this (in assembler), and I believe to have good reasons not to use it anymore.

Not that it could not be done. The resulting code is not useful since it makes heavy use of banking, so it would have to be changed for each PIC. That is almost unthinkable!
 

takao21203

Joined Apr 28, 2012
3,702
OK, so move along to another thread you don't hate. This is the Original Poster's thread and his project. It is being done in his style.
Yes I know. The thread and the OP project should be fine.
Considering it carefully, I would be very thankfully to have had received such advice some years ago.

Regrets come later when you want to reuse or maintain such an assembler source.

I did not say anything about the thread or individual posts. Only speaking about my own assembler work, and dealing with any of it now.
 

Thread Starter

TCOP

Joined Apr 27, 2011
94
Rich (BB code):
char* get_font_ptr(char* font)
{unsigned int addr;
 addr=*(font+1);addr<<=8;addr|=*(font);
 return(((const char*)(addr)));
}
I am not familiar with C but as I am moving on with my project (it is the biggest one i ever made), I am starting reading few things about it.
Could you comment your code to be easier for me to understand?
I cant understand how get_font_ptr holds the 16bit address at the end.
 
Last edited:

ErnieM

Joined Apr 24, 2011
8,415
I am not familiar with C but as I am moving on with my project (it is the biggest one i ever made), I am starting reading few things about it.
Could you comment your code to be easier for me to understand?
I cant understand how get_font_ptr holds the 16bit address at the end.
C uses the asterisk * to indicate indirection, so the routine is a true function (as it has a return value). That return value is:

"char* get_font_ptr(char* font)"

meaning a pointer to a character.


When a function hits a return line it exits and returns the value of that statement, which here is:

return(((const char*)(addr)));

That said, takao21203 would have to explain the purpose of this code as the "font" entity is not defined. I suspect there are other ways to accomplish what he is doing.

(One may also note there are 6 unnecessary parenthesis in that return statement).
 

Thread Starter

TCOP

Joined Apr 27, 2011
94
C uses the asterisk * to indicate indirection, so the routine is a true function (as it has a return value). That return value is:

"char* get_font_ptr(char* font)"

meaning a pointer to a character.


When a function hits a return line it exits and returns the value of that statement, which here is:

return(((const char*)(addr)));

That said, takao21203 would have to explain the purpose of this code as the "font" entity is not defined. I suspect there are other ways to accomplish what he is doing.

(One may also note there are 6 unnecessary parenthesis in that return statement).


And what is the difference to :
char get_font_ptr (char font)
{
unsigned int addr;
addr=*(font+1);
addr<<=8;
addr|=*(font);
return addr;
}
 

ErnieM

Joined Apr 24, 2011
8,415
Only difference I spot is the * is deleted:

char get_font_ptr (char font)
...

That returns an 8 bit signed entity.

char* get_font_ptr (char font)
...

returns the address (ie, a pointer) to where a character is stored.
 

takao21203

Joined Apr 28, 2012
3,702
Here the latest revision where a construct like the above is used in actually working code:

Rich (BB code):
unsigned int addr;
const char* t1=&msg_arr[str_nr];
 addr=*(t1+1);
 addr<<=8;
 addr|=*(t1);
 curr_msg_ptr=(((const char*)(addr)));

while(1){
 curr_chr_data=*(curr_msg_ptr+curr_msg_char);
 if(curr_chr_data==0)break;
 curr_chr_data1=reloc_ascii(curr_chr_data);
 t1=&alpha_chr[curr_chr_data1];
 addr=*(t1+1);
 addr<<=8;
 addr|=*(t1);
 curr_chr_ptr=(((const char*)(addr)));

 curr_chr_size=*curr_chr_ptr;

    for(i=0;i<curr_chr_size;i++)
    {
        curr_chr_data1=*(curr_chr_ptr+i+1);
        store_ram(ram_addr,curr_chr_data1);
        ram_addr++;
1.

Rich (BB code):
unsigned int addr;
const char* t1=&msg_arr[str_nr];
 addr=*(t1+1);
 addr<<=8;
 addr|=*(t1);
 curr_msg_ptr=(((const char*)(addr)));
msg_arr[] is a string table in ROM.

I have examined how the string handling works internally.
A 16bit table is maintained, with reference to page, and offset.
The compiler will also maintain a small piece of code (I call it the string fetcher). Page and offset are loaded into file registers, then this string fetcher is called. It will jump into RETLW table, and then return from there.

There are two types of string tables:

-16bit reference entries
-8bit data using RETLW instructions

The current XC8 compiler can not dereference string tables actually for Baseline PICs.

So, the 16bit pointer must be obtained manually.

2.

Rich (BB code):
 curr_chr_data=*(curr_msg_ptr+curr_msg_char);
 if(curr_chr_data==0)break;
 curr_chr_data1=reloc_ascii(curr_chr_data);
 t1=&alpha_chr[curr_chr_data1];
 addr=*(t1+1);
 addr<<=8;
 addr|=*(t1);
 curr_chr_ptr=(((const char*)(addr)));

 curr_chr_size=*curr_chr_ptr;
The pointer obtained is then used for de-referencing. curr_msg_char is a numeric index that is added to get characters from the individual strings.

Then a translation is done for ASCII. The reason for this is to maintain a font table with less than 256 entries (for instance only upper case characters, and a few symbols).

The value obtained from this is an index into the font bitmap table.
For some purpose we can treat this bitmap table the same as a string table.

Then again another 16bit pointer is obtained, pointing into the data for the ASCII indexed character. The first byte is the actual width of the character (1 scanline = 1 byte).

So
Rich (BB code):
 curr_chr_size=*curr_chr_ptr;
obtains this data byte, containing the width of the character bitmap.

3.

I hope the usage of english language is not too much derivating from what is acceptable (and readable). I do not use translation software for writing replies such as above. No dictionary is used either.
 

BMorse

Joined Sep 26, 2009
2,675
I hope the usage of english language is not too much derivating from what is acceptable (and readable). I do not use translation software for writing replies such as above. No dictionary is used either.
Apparently you took offense to my question..... And as I said, I was just curious, did not know if you were from a region that spoke Irish Gaelic, Ullans or even Cant..... So if my question offended you, it was not meant to, I figured most forum members would be mature enough to answer a simple question without taking it so way out of context. :rolleyes:
 

takao21203

Joined Apr 28, 2012
3,702
Apparently you took offense to my question..... And as I said, I was just curious, did not know if you were from a region that spoke Irish Gaelic, Ullans or even Cant..... So if my question offended you, it was not meant to, I figured most forum members would be mature enough to answer a simple question without taking it so way out of context. :rolleyes:
So it was wise not to reply further to the thread. I just took 2 days absence from the forum.

But reading your reply now, I must say this one is qualified, acceptable level of conversation.

I hope we can work together on topics, at least to a degree, that (or if) you can follow my explanation.

I try to use correct grammar.

But similar to programming construct, I need to improve it, and spend time thinking on writing.

I only write automatically 150 chars/minute, without thinking.
So there can be minor grammar derivation.
Since sometimes I think it is programable.

I mean, my writing can include abstract concepts which are not automatically obvious to all population groups.
 

takao21203

Joined Apr 28, 2012
3,702
Apparently you took offense to my question..... And as I said, I was just curious, did not know if you were from a region that spoke Irish Gaelic, Ullans or even Cant..... So if my question offended you, it was not meant to, I figured most forum members would be mature enough to answer a simple question without taking it so way out of context. :rolleyes:
The Utreans.
 

Thread Starter

TCOP

Joined Apr 27, 2011
94
ok...
I ve done menu and sub menus. I've even made a simple machine state routine to change the key behaviour. So I am at this point:

Screen1
1. Set Time
2. Set Date
3. Set Timers
4. retrun

Screen 2
3.1 Set Timer1
3.2 Set Timer2
....
3.10 Set Timer10

Screen 3
3.1.1. Set Tmr1 On
3.1.2. Set Tmr1 Off
3.1.3. Set Tmr1 Days
3.1.4. Save&Return
When user selects one of the above 3.1.x choices, then it appears in the LCD:
hh:mm:ss
^
or
SMTWTFS
^
and the cursor now moves right and left. On enter, the value that is pointed by the cursor is increased. I need an idea about how to handle this.
My problem is that I have different min-max values on each case and even true/false in case of "Set Days".
 
Last edited:

ErnieM

Joined Apr 24, 2011
8,415
When user selects one of the above 3.1.x choices, then it appears in the LCD:
hh:mm:ss
^
or
SMTWTFS
^
and the cursor now moves right and left. On enter, the value that is pointed by the cursor is increased. I need an idea about how to handle this.
My problem is that I have different min-max values on each case and even true/false in case of "Set Days".
With only 3 buttons to set the time I would keep the ENTER button as accept & advance, and the up down to change the value. Once the cursor advances past the end it either returns to the previous menu or gives a keep it(?) yes/no choice.
 

takao21203

Joined Apr 28, 2012
3,702
You need to store datatype, variable address, as well min/max in the LCD menu table. As well you need a parser for this to take appreciate action.

I have done that in assembler some years ago.

Datatype maybe sounds more complicated than it is. You could use 8-bit for binary as well, just set min=0, and max=1.
 

Thread Starter

TCOP

Joined Apr 27, 2011
94
@ErnieM
Nice.

@takao21203
please explain further.
In case of "Time", i need three variables (hh,mm,ss) ,3 min values, 3 max values =Total 9 bytes. In case of "Days" I need 7 variables etc.
And What if i add something in the future?
I thought that my structure should have a fixed size.

supply as many details as possible
 
Last edited:

Thread Starter

TCOP

Joined Apr 27, 2011
94
perhaps i should explain more what i ve done so far.
I've finally made an LCD table of 8 bytes.
b1:eek:ffset of text
b2:segment of text
b3:min menu
b4:max menu
b5:submenu on enter
b6:menu on back
b7:eek:ffset of on enter routine
b8:segment of on enter routine.

So when I press "Set Tmr1 on", i call a routine that clears screen, prints the 1st line with the current time, places the arrow pointer on second line,changes the state machine and fixes a 16bit variable with the permitted cursor positions.
 

takao21203

Joined Apr 28, 2012
3,702
Well you have to work this out for yourself to some degree.

It is of course adviseable to keep structure sizes all the same.
Not individual variables are used normally.
The setting value is using a "variable", yes, but this could be any kind of RAM.

In the LCD menu data structure, you store just the address of the variable. Means the bank, and the offset. Is particulary bad on the 16f628. If you use extended midrange, at least two regular 16-bit FSRs are available.

I mean in the menu table you store the address (since it is usually in ROM), the min/max value, and eventually datatype (numerical or binary).

Menu entries also can point to other, submenu entries.

It is fairly complex in assembler. Nowadays I would not really want to do it in assembler. In addition there will be banking issues as well.
 
Top