Searching \ for 'Reusing Subroutines --' in subject line. ()
Make payments with PayPal - it's fast, free and secure! Help us get a faster server
FAQ page: www.piclist.com/techref/index.htm?key=reusing+subroutines
Search entire site for: 'Reusing Subroutines --'.

Truncated match.
PICList Thread
'Reusing Subroutines --'
1997\08\06@164409 by Shane Nelson

flavicon
face
Hi,

I'm trying to create a nice library of subroutines,
but when reusing them I find it a pain to have to
go through all the EQU's, to make sure I'm not already
using those locations in another part of my program.

My question: is it possible to have the compiler
dynamically allocate memory in some way?

ie)
Instead of having to do this:

COUNT   equ     h'7'
ACCA    equ     h'8'
;16bit
ACCB    equ     h'9'
ACCD    equ     h'a'

I want to be able to get away with something more like this:

db = define byte
dw = define word

db      COUNT
dw      ACCA
db      ACCB
db      ACCD

-----

Then, instead of hard coding variables it could just use the
next available space.


Thanks in advance.
-Shane.

1997\08\06@171025 by Alan G. Smith

flavicon
face
Use the CBLOCK statement in MPASM.

Hope this helps,

Alan G. smith

+---------------
| Alan G. Smith
| spam_OUTagsTakeThisOuTspampoboxes.com
| http://www.innovatus.com/ags

On Wed, 6 Aug 1997, Shane Nelson wrote:

{Quote hidden}

1997\08\06@172823 by Eric van Es

flavicon
face
Shane Nelson wrote:

{Quote hidden}

Why don't you define the last couple  of
RAM addresses as local variables.
Ie:
local1 equ 2f
local2 equ 2e
etc

Using the SET command to equate your local
(library) variables to those global ones.
Ie:
count set local1
acca set local2

--
Eric van Es               | Cape Town,
South-Africa
Mailto:.....vanesKILLspamspam@spam@ilink.nis.za | WWW:
http://www.nis.za/~vanes/
TEMPORARY/HOLIDAY ACCOMMODATION?
http://www.nis.za/~vanes/accom.htm

1997\08\06@223622 by unthiti Patchararungruang

flavicon
face
On Wed, 6 Aug 1997, Shane Nelson wrote:

{Quote hidden}

       I think no compiler can do this. However, you can change the value
of "d_" statement like:

  test    dw    3

             .
             .
          movf  test, f
             .
             .


Sunthiti

1997\08\06@231605 by Andres Djordjalian

flavicon
face
Hi everybody!

I'm working on a project that's quite long (about 10K lines of code)
for a moving-message-LED-display controlled by a 16C73 that has a lot
of features. It's working nicely and it's almost finished.

While working on it I developed an incredibly-simple-but-suprisingly-
useful method for the allocation of registers. Without it I would
have had a hard work integrating all its components. It consists of
several macros of almost unreadable definition comprised in a <200
line include file I call "ALOCATOR.MAC". When you use them the rest
of the code is very readable and the registers are allocated and
overlapped almost optimally. And the idea is *very* simple. So much
that I don't know how I didn't think of it before! (...and I don't
believe nobody else has..)

I've been thinking of trying to write an article but I desperately need to
finish this project cause I am having some trouble with my finances. On
the other hand you might have noticed that I am not a native
English-speaker and I don't have much confidence in the quality of a work
of mine written in English...

Instead, I like the idea of explaining it on this list. I see no point
in keeping it to myself! I'm starting on a separate message, and if you
find it interesting I would very much appreciate your feedback.

                                                           Andres


A METHOD FOR PIC ASSEMBLY CODE INTEGRATION AND REUSE - Part I

As you may guess from the fancy title, the method I'm suggesting deals
with modularization of programs, which means to view a system (the
program) as a structured collection of components (routines or sets of
routines).

Most of the structure is defined by a client-server relation between
components. If for a given a component there is another that uses it we
say that the first is the server and the second the client in this
relationship. A component may have several clients and several servers
too.

It is illustrative to graph this structure in an orderly manner, putting
the clients on top of the servers. For example:


                           TEXT_DISPLAY
                             |      |
               ---------------      ------------------
               |                                     |
               |                                     |
     FILLING_A_TEXT_BUFFER-------------    READING_A_TEXT_BUFFER
           |         |                |              |
           |         -------------    ------------   |
           |                     |               |   |
   KEYBOARD_INTERFACE     SERIAL_INTERFACE     EEPROM_SUPPORT


A critical issue in programming a system like this is the allocation of
variables. By that I mean giving an address to each variable each
component uses.

If you use no strategy at all and allocate everything by hand using "EQU"s
you will have to do some work every time you create a new variable. Lets
say you are working with the keyboard interface module in the previous
example and need a new variable for it. What address will you use? You
would have to run through all the other source code to find an unused one.
Or you could do the same thing with less work if you allocate the
registers in order and write down the next free one. But what happens when
you want to use the keyboard interface in another project, maybe with
other components you had also already programmed? It's a lot of work,
and it's a routine that requires none of your creativity. Hey! What do you
have your PC for?

The same scheme can be automatized in several ways. One is what Microchip
suggests with the use of unseeded "CBLOCK"s. With this methods the
assembler will linearly assign addresses to all variables at compile-time.
That solves the problems as long as you don't run out of registers.

If you do run out of registers the solution will surely be overlapping
(assigning the same address to) variables that are never "active" at the
same time. That's where the method I'm suggesting comes to play. It is a
way of automatizing this job without sacrifizing any modularity.

The implementation of the method consists of several macros that give a
high-level-language look to the declaration of variables in your code and
make most of the job for you.

I'll give you just a tip here and I'll continue in other messages. The
question is: What variables can you overlap? Recall the previous graph.
You can't be sure if you can overlap variables belonging to
"FILLING_A_TEXT_BUFFER" with others from "KEYBOARD_INTERFACE" because both
components might be active at the same time. I mean, "FILLING..." could be
active waiting for "KEYBOARD..." to perform a service, and "KEYBOARD..."
would then be active performing it.

But notice that surely "KEYBOARD..." and "SERIAL_INTERFACE" will never be
active at the same time! So you may overlap their variables. This has some
exceptions to which I will refer to later.

That is the heart of the method, I recognize it is an almost stupid idea
and that I'm not discovering America but it took quite a while for me to
sit down, think and develop it, and its conclusions turned out being very
useful to me.

If I don't receive complaints for using so much of this space I'll
continue later. I will be glad to receive feedback from you. Do you have
any other strategies for doing this? Can you see something wrong in what
I'm saying? Did you think of this too?

Regards!
                                                           Andres


A METHOD FOR PIC ASSEMBLY CODE INTEGRATION AND REUSE - Part 2

First of all I'd like to publicly thank again for all the positive words
many of you had towards my first message.

In my last posting I ended by giving an example of a case in which you
may overlap variables. We had the following structure as an example:


                           TEXT_DISPLAY
                             |      |
               ---------------      ------------------
               |                                     |
               |                                     |
     FILLING_A_TEXT_BUFFER-------------    READING_A_TEXT_BUFFER
           |         |                |              |
           |         -------------    ------------   |
           |                     |               |   |
   KEYBOARD_INTERFACE     SERIAL_INTERFACE     EEPROM_SUPPORT


I said that you shouldn't ovelap the variables belonging to
"FILLING_A_TEXT_BUFFER" with those from "KEYBOARD_INTERFACE" because both
components might be active at the same time.

By "active" I mean that the component is performing a service for a
client. For example, if in this structure the program flow is in the
"KEYBOARD_INTERFACE" that component will be active and "FILLING_A_TEXT_
BUFFER" too because it's waiting for "KEYBOARD..." to perform the service
it requested (no other component could have requested a service from
"KEYBOARD..." with this structure).

The rule is to overlap variables that belong to components that are never
active at the same time, with some exceptions to which I'll soon refer.

To check for the validity of this assertion lets make an analogy with an
Algol-like-high-level-language (one that makes use of a data stack)
running on a language-oriented-machine (one that HAS a data stack!).

If a routine is active it has a stack frame belonging to it where it keeps
its local variables. When this routine calls a server its frame is kept on
the stack and the server's frame is placed on top. So when the program
flow is in the server both routines are active, as in the example, and the
memory for their local variables is not overlaped.

What happens when the server ends its job, the flow returns to the
original routine and it calls another server? The first server's stack
frame is wiped out and the newer server's is put on top of the original
routine's. So you may have some memory space being shared by both servers.

But there was no problem about it because both routines were not active at
the same time, and if one never calls the other, and never calls another
routine that calls the other, and never calls another routine that calls
another routine... etc. that calls the other, then you can assure that you
will *never* need separate memory locations for these routines' variables.

Our example shows this behaviour for {"KEYBOARD...", "SERIAL..." and
"EEPROM..."} and for {"FILLING..." and "READING..."}. But there is no need
to inspect the client-server structure of a given system to apply the
method, I'm explaining this only to introduce its logic but its
application is much simpler.

I'll again begin with an example and generalize later on my next posting.
Lets say each component has the following quantity of local variables:

"KEYBOARD_INTERFACE": 5
"SERIAL_INTERFACE": 3
"EEPROM_SUPPORT": 3
"FILLING_A_TEXT_BUFFER": 7
"READING_A_TEXT_BUFFER": 4
"TEXT_DISPLAY": 3

I'll start from the bottom of the structure. Where will I put the
variables belonging to the first three components? I already said I could
overlap them.

Recall the method to which I refered on my last posting that assigned
consecutive addresses to variables and could be implemented with
"CBLOCK"s. I said that it was OK as long as you did not run out of
registers, because it did not overlap any. So lets add this feature to
that method.

But first lets agree on something, when you use consecutive addresses it's
the same to build from the bottom to the top incrementing the addresses or
to do it from top to bottom decrementing them. This time I'll choose the
second option, but for no particular reason.

So, if the maximum register address is 127, the variables belonging to
"KEYBOARD..." will occupy positions 123 to 127. And the other two will
also begin in 127 as I have no problem in ovelapping them! "SERIAL..."
will use 125 to 127 and "EEPROM..." too.

Now the responsibility of not overlapping that allocated space is passed
to the components on top. This makes sense if you think that the
definition of a component includes information of its servers but not of
its clients.

Where can I put "FILLING..."'s variables? They shouldn't overlap the first
three allocated spaces, so they could start at 122 being the next free
position that meets this condition. The variables will go from 116 to 122.

"READING..."'s variables must not overlap only "EEPROM..."'s, so they can
start at 124, occupying from 121 to 124.  "TEXT..."'s variables shouldn't
overlap any of the others' so they may start at 115 and run down to 113.

The general rule then is (if you're using a "decremental model" as in
this case): allocate beggining from the least of the routine's servers'
allocated positions minus one. This is a job for a macro.

I'll continue later, but I guess you can see much of the point already. I
also owe you the explanation of the exceptions for this scheme. Once
again, I'll appreciate any opinions.

Regards!
                                                           Andres


A METHOD FOR PIC ASSEMBLY CODE INTEGRATION AND REUSE - Part 3

I ended my last message by giving a golden rule for the allocation of
variables: begin to allocate from the least of the routine's servers'
allocated positions minus one (if you are using a "decremental model").

So, what do we need to define a component's variables? We need:

* Their names (for their sizes I use only bytes but you could make a
 scheme in which you could fix the variable's sizes)
* Someway of knowing which variables belong to each component, this
 can be done with some sort of "frame delimiters".
* We need to know what servers the component has.

A way of doing this could be, for "FILLING..." in my previous posting's
example:

       FRAME   FILLING
       USES    KEYBOARD
       USES    SERIAL
       USES    EEPROM
       BYTE    fillingsVarNo1
       BYTE    fillingsVarNo2
       ...etc
       FEND    FILLING

I think it looks pretty good. "FRAME", "USES", "BYTE" and "FEND" are
macros that implement the method I've been describing on my previous
echos.

Before showing the macros I'll talk about the exeptions for this
allocation scheme. I started the reasoning that ended with it by saying
that you could overlap variables as long as the components to which they
belong are never active at the same time. This is not true with some
variables.

Lets say you have a counter of some sort. It consists of two routines
"COUNT" and "INFORM". The first, when called, increments the count, and
the latter informs its current value. There's a piece of information
(the count value) that must be kept even when the routines are not active.
So you shouldn't overlap this variable with others although their
components might not share activity.

This fact is not a result of the method or a derivative of Microchip's
architecture, it is inherent to the type of variable and, for that reason,
high-level languages provide support for them. In my opinion, the cleanest
way to deal with them is what Ada and OOP languages do with instance
variables. See how they have to do with a certain state the component has
that outlives the execution of its routines. The C language, on the
other hand, supports this kind of variables with the "static" modifier.

In compiler theory a variable is "static" if its destination and size is
already defined at compile-time. It's very probable that an implementation
of C will deal with variables declared as static by placing them on a
location defined at compile time, differently from automatic variables
(those which were not defined as static) that are placed dynamically on
the stack as I briefly explained on my last posting.

In my opinion the architects of the C language shouldn't have used a term
that relies on a particular implementation and refers to a low-level
issue, and has not much to do with the real behaviour the programmer is
asking for. But this is just another criticism to C to be added to the
long list...

I started using "STATIC" too as a name for the macro to support these
variables, but I don't want to imitate what I think is wrong so I'm
switching to "PERSISTENT". I shouldn't use "STATIC" also because, strictly
speaking, all the variables in the method are static!

So when the byte to declare should outlive the component's execution I put
"PERSISTENT variable" instead of "BYTE variable".  As you may expect,
"PERSISTENT" is another macro.

Where to put these persistent variables? We shouln't overlap them with
anything, so the best we can do is assign consecutive addresses for them
that are apart from those we used for the automatic variables.

I said before that it was the same to assign upwards from the bottom than
downwards from the top as I eventually did, so to pile the persistent
variables we could use the first option this time.

For example, if both "KEYBOARD..." and "EEPROM..." have also got a
persistent variable each, and the first available location is number 32,
we could assign one in register 32 and the other in 33. Persistent
allocation consumes more RAM resources as it won't be overlapped.

Most of the times the life of persistent data, although not bounded by
the execution of the routines to which they belong, can be bounded to the
execution of a certain component. For example in OOP instance variables
overlive the execution of the methods that make use of them but are
created and destroyed in conjunction with the objects they belong to.

This sort of thing is not implemented in this method, persistent variables
live through the whole execution, and as we could save RAM there seems to
be room for improvement here.

Before ending, notice that a "memory full" condition will occur if,
during compilation, the incrementing count of positions for persistent
variables collides with the minimum decrementing count for automatic ones.

Now I'm ready to list the most important macros, "FRAME", "USES",
"BYTE", "PERSISTENT" and "FEND". I'll be doing it in a separate message.

I'll be back soon!

                                                         Andres
                                                 adjordjspamKILLspamaleph.fi.uba.ar


A METHOD FOR PIC ASSEMBLY CODE INTEGRATION AND REUSE - Part 4

----------------------- cut here ----------------------------------------

; ****************************************************************
; *  MACROS FOR THE ALLOCATION OF VARIABLES                      *
; ****************************************************************
;
;  Andres Djordjalian
;  EMail: .....adjordjKILLspamspam.....aleph.fi.uba.ar
;

   NOEXPAND    ; I strongly suggest it as listings could get rather
               ; long and unreadable with macro expansions

; *************************************
; *  FRAME                            *
; *************************************

FRAME                MACRO      rutina

   IFDEF   alocTemp
#undefine   alocTemp
   ENDIF
#define     alocTemp    rutina#v(CERO)#v(CLAVE)
VOLATILES0      SET     H'7F'
VOLATILES1      SET     H'FF'
alocTemp        SET     0

                    ENDM


; *************************************
; *  USES                             *
; *************************************

USES                MACRO       rutina

#undefine   alocTemp
#define     alocTemp    rutina#v(CERO)#v(CLAVE)
   IF VOLATILES0 > alocTemp
VOLATILES0      SET     alocTemp
   ENDIF
#undefine   alocTemp
#define     alocTemp    rutina#v(UNO)#v(CLAVE)
   IF VOLATILES1 > alocTemp
VOLATILES1      SET     alocTemp
   ENDIF

                   ENDM


; *************************************
; *  BYTE variable                    *
; *************************************

BYTE            MACRO           variable

   IF    VOLATILES#v(RAM_PAGE) > PERSISTENTES#v(RAM_PAGE)
variable    EQU     VOLATILES#v(RAM_PAGE)
   IF    VOLATILES#v(RAM_PAGE)<MIN_VOLATILES#v(RAM_PAGE)
MIN_VOLATILES#v(RAM_PAGE)  SET VOLATILES#v(RAM_PAGE)
   ENDIF
VOLATILES#v(RAM_PAGE)   SET   VOLATILES#v(RAM_PAGE)-1
   ELSE
   ERROR("Memory full, can't allocate automatic data")
   ENDIF

               ENDM


; *************************************
; *  PERSISTENT variable              *
; *************************************

PERSISTENT      MACRO           variable

   IF      PERSISTENTES#v(RAM_PAGE) < MIN_VOLATILES#v(RAM_PAGE)
variable        EQU     PERSISTENTES#v(RAM_PAGE)
PERSISTENTES#v(RAM_PAGE)   SET   PERSISTENTES#v(RAM_PAGE)+1
   ELSE
   ERROR("Memory full, can't allocate persistent data")
   ENDIF

               ENDM


; As you may expect, this needs initializing:

; *************************************
; *  INITIALIZING ALLOCATOR MODULE    *
; *************************************

PERSISTENTES0   SET     H'20'           ; Reconfigurate this initial
PERSISTENTES1   SET     H'A0'           ; values to the target machine.
VOLATILES0      SET     H'7F'           ; The values given here are for
VOLATILES1      SET     H'FF'           ; a 16C73 or 16C65
MIN_VOLATILES0  SET     H'7F'
MIN_VOLATILES1  SET     H'FF'
RAM_PAGE        SET     0
CERO            EQU     0
UNO             EQU     1
CLAVE           EQU     86

----------------------- cut here ----------------------------------------

Well, these are the routines I'm using. There is much more to say about
the application of these macros, and there is another macro I find quite
useful for the allocation of flags, so I'll be continuing with this
postings if there's interest.

As always, I'll appreciate any opinions. Regards!

                                                      Andres
                                              EraseMEadjordjspam_OUTspamTakeThisOuTaleph.fi.uba.ar


A METHOD FOR PIC ASSEMBLY CODE INTEGRATION AND REUSE - Part 5

===Erratas for my last posting============================================

I forgot to list the "FEND" macro!

----------------------------- cut here ----------------------------------

; *************************************
; *  FEND                             *
; *************************************

FEND                MACRO      rutina

#undefine   alocTemp
#define     alocTemp    rutina#v(CERO)#v(CLAVE)
alocTemp    SET     VOLATILES0
#undefine   alocTemp
#define     alocTemp    rutina#v(UNO)#v(CLAVE)
alocTemp    EQU     VOLATILES1

                   ENDM

----------------------------- cut here ----------------------------------

Another one: the initialization of "VOLATILES0" and "VOLATILES1" is
not necessary.  Instead, you can assign those values to "INIT_VOLATILES0"
and "INIT_VOLATILES1":

----------------------------- cut here ----------------------------------
INIT_VOLATILES0     EQU     H'7F'
INIT_VOLATILES1     EQU     H'FF'
----------------------------- cut here ----------------------------------

...and use these constants in the "FRAME" macro:

----------------------------- cut here ----------------------------------

; *************************************
; *  FRAME                            *
; *************************************

FRAME                MACRO      rutina

   IFDEF   alocTemp
#undefine   alocTemp
   ENDIF
#define     alocTemp    rutina#v(CERO)#v(CLAVE)
VOLATILES0  SET     INIT_VOLATILES0
VOLATILES1  SET     INIT_VOLATILES1
alocTemp    SET     0

                    ENDM

----------------------------- cut here ----------------------------------

...so as to have all the machine-dependent constants in the initialization
part.

One more. The initialization of "MIN_VOLATILESx" must be incremented by
one to:

----------------------------- cut here ----------------------------------
MIN_VOLATILES0  SET     H'80'
MON_VOLATILES1  SET     H'100'
----------------------------- cut here ----------------------------------

...or otherwise if there are no volatile variables you wouldn't be able
to assign the last memory position to a persistent one.

===End of the erratas for my last posting=================================

Since I began with the analysis of the problem I'm talking about
"components" but I haven't specified accurately what I mean by such.
I'll do it now and start with some practical matters.

When I said "a component" I was simply refering to a part of a system. It
could be an large include file or just a short routine. All that I've
written about doesn't depend on the level of granularity the system
representation has. And the links between these components can be one
subrutine call or several.

Of course, using the method with a system representation of a higher
granularity (or to put it in other words, simply to build more frames,
for every single routine for example) will result in an better use of RAM,
but you'll have to write more.

The options seem to be what I've just mentioned, to build a frame for each
routine, or making a frame for a whole include file. By the way, I suggest
to divide your program into reusable include files for all this to make
better sense. Having an include file with a global frame and one or more
routine frames for particular routines seems to me hard to follow, and as
there is no error checking for missing "USES ..." I think it would surely
cause more than one headache.

This is getting a bit long, I'll continue on another message. I'll be
back soon!
                                                           Andres


A METHOD FOR PIC ASSEMBLY CODE INTEGRATION AND REUSE - Part 6

On my last posting I wrote about two ways in which frames could be built.
One is to build a frame for a whole include file, for example:

------ cut here (beginning of an include file)---------------------------

; ***********************************************************************
; * MODULE THAT STRUCTURES AN I2C EEPROM IN BLOCKS AND CELLS  (KEY EEP) *
; ***********************************************************************

   IFNDEF  I2C_INTERFACE
       INCLUDE "I2C.INC"
   ENDIF

RAM_PAGE        SET     1
       FRAME   EEP
       USES    I2C
       BYTE    EEPblock
       BYTE    EEPcell
       BYTE    EEPregister
       FEND    EEP

; **************************************************
; *  SAVE EEPregister IN [EEPblock,EEPcell]        *
; **************************************************

EEPSaveBC:      MOVF    EEPblock & H'7F',W                ;  ...etc
------------------------------- cut here --------------------------------

Here there's only one frame named "EEP" for all the routines in the file.
The routines use the component "I2C.INC", which also has only one frame,
named "I2C". You can deduct this from the "USES..." line.

Similarly, if there's another component that uses this one, the frames
where the variables belonging to a routine that uses any of these are
defined must have a "USES EEP".

The other option looks like this:

---- cut here (the middle of another include file)------------------------

; **************************************************
; *  FORMAT                                        *
; **************************************************

       FRAME    BI2Format
       USES     EEP
       BYTE     BI2J
       BYTE     BI2I6
       FEND     BI2Format

BI2Format:      CLRF    EEPregister & H'7F'
               MOVLW   1
               MOVWF   EEPblock & H'7F'
               CLRF    EEPcell & H'7F'
               CALL    EEPSaveBC                     ; etc...
------------------------------- cut here --------------------------------

This is the beginning of a routine that's part of a component (from now on
I'll be calling a "component" an include file that adds a particular
feature to a system, like an interface, a data structure, etc, to
differentiate it from a routine)

I was saying that this routine is part of a component that uses the
previous component. But in this case the frames are declared for each
routine. The routine "BI2Format" uses the previous component (look at the
last line listed) and that's declared on the beginning of its frame. Of
course (didn't I mention this?) "USES..." clauses must precede "BYTE..."
declarations!

BI2Format uses EEP's variables as an interface. That's the way to do it
but, as these variables are not persistent, you should be careful. You
mustn't call another subroutine and pretend that their values remain
unaltered.  For example, you shouldn't code something like...

------------------------------- cut here --------------------------------
               MOVLW   1
               MOVWF   EEPblock & H'7F'
               CALL    KEYWhatever
               CALL    EEPSaveBC
------------------------------- cut here --------------------------------

This may not work as KEYWhatever might alter EEPblock's contents! It's not
hard to get used to avoiding this practice. If you need to mantain the
contents of an interface variable between other subroutine calls you
should use another variable (for example, BI2saveEEPblock in this case).
Changing EEPblock to a persistent variable would work but it would be hard
to follow and might increase RAM usage.

There are two things I want to say about this posting's code that remain
pending. One is the scheme I'm using to name variables, the other the
"& H'7F'"s. I'll continue later.

Is my explanation on where to build frames clear? As always I appreciate
your opinions. Regards,

                                                         Andres


A METHOD FOR PIC ASSEMBLY CODE INTEGRATION AND REUSE - Part 7


Names for variables
-------------------

If you automatize the task of allocating registers you'll very probably
have more variables than if you don't. That's because you'll be able to
create more variables and also because this scheme is intended to
facilitate code reuse and for that you shouldn't share variables.

So variable naming becomes and issue. You've already automatized the
allocation of registers and facilitated reuse, now you don't want to be
dealing with the names of variables, checking to see that they don't
repeat. And the same thing happens with routines' names.

The solution I'm using is giving every component a three capital letter
keycode. For example, "KEY" for a keyboard interface, "MDI" for a
multiplexed-display driver, etc. I document this keycode at the beginning
of the source file, and when I do a new one I care that it is unique.

Every label belonging to a source code uses its keycode as a prefix. For
example "MDIdigit", "KEYauxi", "KEYReadKey", etc.

For the rest of the names I use a common convention: start with a capital
letter if it's a routine or with a small letter if it's a variable. To
join several words into a label, instead of using an ugly underscore,
start the new word with a capital letter.

I use this rule only because of a personal preference. What I do want to
recommend is the use of the keycode to deal with the fact that all
variables have a global scope. If you can think of another way please let
me know.

I think this solution is pretty neat, and it has another advantage. If you
are browsing through a symbol list looking for the values given to a
component's labels you just have to search for the keycode and there
you'll have all that component's symbols together.


Dealing with RAM paging
-----------------------

Ups, I haven't got much to say about this. The macros, as I'm using them
now, give more-than-7-bit addresses. What I mean is that a variable
allocated on page 1 could have, for example, address A7 (not 27). So, if
you compile something like "MOVF thatVariable,W" you will get a message.

Surely the macros could be modified to give 7-bit-addresses, but I don't
like it because symbol lists would give less information and I have the
idea that source code could get rather confusing...  I really didn't think
much about it.

What I do is write "MOVF thatVariable & H'7F',W" to avoid the messages for
those lines where I had taken care of ram paging. Typing those "& H'7F'"
looks like a lot of nonsense work but with a reasonable editor you can do
text subsitutions or macros and save much of it. And on the other hand,
on the source code you can distinguish between page 0 and page 1 variables
and you still have messages alerting you of those cases to which you
didn't pay attention.

But I think there could be something better, perhaps using "#define"s.
By the way, I have never used four-ram-page processors, and this would not
be very neat with them I guess...


Dealing with ROM paging
-----------------------

The ROM pages where components are placed can be documented on a
client-server structure diagram like the one I sketched on my first
posting.

As the reasonable thing is, given a client-server relationship, to try to
place both components on the same page so as to save changes to PCLATH,
the whole structure would have only a few clusters of components belonging
to the same page. So with a quick look you can tell where you need to
change PCLATH and where each component is placed.

I'm not using any automated scheme to change PCLATH, mostly because many
times I do several sequential long calls and changing PCLATH for every
call there would be a waste of ROM, a resource I always run short of.

Problems could arise when you want to reuse code and you find that if you
mantain the original page assignments the code won't fit, or if you want
to use a piece of code made for page 1 of a two-page processor in a
one-page chip.

For those cases I guess that I would do second versions of those
components that need to be changed. For example, if I did a display driver
for page 1 and I want to change it for page 0 I'd copy that source and its
clients and servers naming them differently (changing the key-code would
not be necessary) and take out and add the PCLATH assignments that would
be needed. Then, if I have a third project needing this driver, I'd have
two versions from where to choose.

This message might have more questions than answers but I already told you
that was my objective. I have been refining my ideas as I used them and I
would like this process to continue here with yours!

On my next posting I'll list a macro I use to allocate flags. If you have
any questions or ideas about the variable-allocation scheme please write.

Regards!
                                                         Andres
                                                 adjordjspamspam_OUTaleph.fi.uba.ar


A METHOD FOR PIC ASSEMBLY CODE INTEGRATION AND REUSE - Part 8

Allocating flags
----------------

If you are programming for code reuse it doesn't look correct to assign
flags belonging to different components with something like:

   PERSISTENT flags
#define     MDIerror        flags,0
#define     KEYcapsLock     flags,1
#define     EEPerror        flags,2 ...etc.

...because for a different project you might need only some of these
components along with others, and that would force you to reassign
flags by hand, changing them in duplicate source files or doing all
allocations in the main source file. None of these look very neat nor
practical.

On the other hand, placing each component's flags on a local
non-persistent variable would be a waste of RAM if you are to assign only
two or three flags for each component, which I consider normal.

A macro that allocates flags would be useful, and making it is no big
deal.

As for now I'm not differentiating persistent flags from non-persistent
because I don't see much to win. The reason to allocate non-persistent
data is to save RAM by permitting overlapping. Flags, being only one bit
long, don't add much unless a component uses a lot of flags, and in that
case those flags could be placed on local non-persistent variables instead
of using the macro. Surely, if you are desperate for RAM and could profit
from it, this macro could be expanded to handle non-persistent flags.

The algorithm I use to allocate the flags is simply the following:

* If there is no persistent byte available for flags allocate a new one
* Allocate the flag on the next bit available on that byte

This is handled by the following macro:

----------------------------- cut here ---------------------------------

; *****************************************
; *  RESERVE_FLAG  nameByte, nameBit      *
; *****************************************

RESERVE_FLAG    MACRO     nombreByte, nombreBit

       IF BYTE_FLAGS#v(RAM_PAGE)==H'FF'
BIT_FLAGS#v(RAM_PAGE)     SET     8
BYTE_FLAGS#v(RAM_PAGE)    SET     0
       ELSE
BIT_FLAGS#v(RAM_PAGE)     SET     BIT_FLAGS#v(RAM_PAGE)+1
       ENDIF
       IF BIT_FLAGS#v(RAM_PAGE)==8
BYTE_FLAGS#v(RAM_PAGE)            SET     BYTE_FLAGS#v(RAM_PAGE)+1
BIT_FLAGS#v(RAM_PAGE)             SET     0
       PERSISTENT  FLAGS#v(BYTE_FLAGS#v(RAM_PAGE))
       ENDIF
nameByte      EQU      BYTE_FLAGS#v(RAM_PAGE)
nameBit       EQU      BIT_FLAGS#v(RAM_PAGE)

               ENDM

----------------------------- cut here ---------------------------------

Which needs the following initialization:

----------------------------- cut here ---------------------------------

BYTE_FLAGS0     SET     H'FF'
BYTE_FLAGS1     SET     H'FF'

----------------------------- cut here ---------------------------------

This, again, is for a two-RAM-page processor. I guess that if you only
add "BYTE_FLAGS2" and "...3" it would work on a four-page.

The macro returns a reference to a register and a bit number for you to
allocate the flag with something like:

RAM_PAGE    SET     0
           RESERVE_FLAG    XXXmyFlagByte,XXXmyFlagBit
#define     XXXmyFlag       FLAGS#v(XXXmyFlagByte),#v(XXXmyFlagBit)

Or, for a 1-page flag and to avoid messages:

RAM_PAGE    SET     1
           RESERVE_FLAG    XXXmyFlagByte,XXXmyFlagBit
#define     XXXmyFlag       FLAGS#v(XXXmyFlagByte) & H'7F',#v(XXXmyFlagBit)

The ideal thing would had been to be able to do something like:

   FLAG XXXmyFlag

...but it is not possible, as far as I know, because the assembler won't
perform substitutions in a "#define". It's a pity, not only it would save
some cut and pasting and typing, it would also be much clearer to read. I
hope I'm missing something or, if I'm not, that future versions of MPASM
include something to handle this.

As always, I appreciate your feedback. Regards!

                                                           Andres
                                                  @spam@adjordjKILLspamspamaleph.fi.uba.ar

1997\08\07@090558 by Andy Kunz

flavicon
face
>My question: is it possible to have the compiler
>dynamically allocate memory in some way?
>...
>Then, instead of hard coding variables it could just use the
>next available space.

Get the HiTech C Compiler and use the macro assembler which comes with it.
It does what you want and more.  IMHO, it's worth the current $300 price in
itself.

It uses a real linker, too!

Andy

==================================================================
Andy Kunz - Montana Design - 409 S 6th St - Phillipsburg, NJ 08865
         Hardware & Software for Industry & R/C Hobbies
       "Go fast, turn right, and keep the wet side down!"
==================================================================

1997\08\08@125532 by Steve Kaufman

flavicon
face
Maybe I am misinterpreting the question. But the Parallax assembler has the
DS (Define Space) directive which has an argument that allows you to specify
the number of locations needed. I use it like this:

;************************
;             RAM
;************************
                               ORG RAM

FLAGS                DS    1            ; 1 location, 8-bit value
TMRCTL            DS    1
MSG_TIMER    DS    2            ; 2 locations, 16-bit value
MSG_BUFF       DS    8            ; 8 locations

The assembler then assigns addresses starting at RAM and allocates the
requested number of locations for each variable in succession.

Not sure if that's what you were asking, but thought I'd throw it out
there...

Steve


{Original Message removed}

1997\08\08@165418 by STEENKAMP [M.ING E&E]

flavicon
picon face
Andres,

Your system of memory allocation seems extremely interesting and
powerfull.  I will give it a try when I work on a (multi module) PIC
project again.  Thanks for sharing it!

Niki

1997\08\13@212514 by Marc 'Nepomuk' Heuler

flavicon
face
Hi Shane (Shane Nelson), in <KILLspamPine.LNX.3.96.970806143100.17103B-100000KILLspamspamcheetah.spots.ab.ca> on Aug 6 you wrote:

{Quote hidden}

Yes, it is possible.  I do it like his (off my head, so prepare for typos):

BOFFSET         SET     0x0c

BYTE            MACRO   LABEL
LABEL           EQU     BOFFSET
BOFFSET         SET     BOFFSET+1
            IF      BOFFSET>0x30
            retlw   ERROR_OUT_OF_REGISTERS
            ENDIF
>             ENDM

STRUCT          MACRO   LABEL,SIZE
LABEL           EQU     BOFFSET
BOFFSET         SET     BOFFSET+SIZE
            IF      BOFFSET>0x30
            retlw   ERROR_OUT_OF_REGISTERS
            ENDIF
>             ENDM


Then I use the macros like shown

            BYTE    CNT                     ; assigns 0x0c to CNT
            STRUCT  Keybuffer,4             ; reserves 4 bytes at 0x0d



Have fun pic'ing

More... (looser matching)
- Last day of these posts
- In 1997 , 1998 only
- Today
- New search...