10-06-2016, 07:27 PM (This post was last modified: 10-06-2016 07:28 PM by Claudio L..)
Post: #409
 Claudio L. Senior Member Posts: 1,769 Joined: Dec 2013
(10-06-2016 02:22 PM)Bruno Wrote:  Hi,

I'm still working on the MEM command to improve it's accuracy (and to improve my knowledge about newRPL internals ), and I need a little more help please.

PART 1 - MEM

{ ... }

Some research for a simple result :
Code:
    case MEM:     {         rplGCollect();         BINT mem = halGetFreePages() << 12;         mem += 4096 - ((DSTop - DStk) & 0xFFF);         mem += 4096 - ((RSTop - RStk) & 0xFFF);         mem += 4096 - ((LAMs - LAMTop) & 0xFFF);         mem += 4096 - ((DirsTop - Directories) & 0xFFF);         mem += 4096 - ((TempBlocksEnd - TempBlocks) & 0xFFF);         mem += 4096 - ((TempObSize - TempOb) & 0xFFF);         rplNewBINTPush((BINT64)mem, DECBINT);         return;     }

But I still have some interogations :

- halGetFreePages() returns 404KB of free ram, so 108KB of memory allocated for a fresh newRPL installation,
but I've identified 'only' 103KB (79 + 24) of different memory regions, it stay 5KB of unknown memory, what am I missing ?

- My guess about the regions ownership (USER vs SYSTEM) helped me to determine for which regions MEM needs to compute the actual use,
but is it my guess right ?

Of course, any comment, correction and best practice about the code are welcome.
You are on the right track.

halGetFreePages() gives you a good idea of how much free memory we have, then you can "add" the few bytes free at the end of each region's pages, which you did correctly.
a) Use TempObEnd instead of TempObSize. In general, for all regions the "Size" allocates some extra slack and it has a granularity to reduce the number of allocations needed from the system, so it doesn't reflect the actual use (look at the growXXX() functions).
b) Don't forget these are WORDPTR, so the difference (TempObEnd-TempOb) is in 32-bit words, not in bytes. So you need to subtract: (1024- ((TempObEnd-TempOb)&0xfff))<<2. This is true for all regions and all pointers.

I'd say this is as accurate as you can get. If you try to get too deep into which regions are what, your code may not be portable to other platforms, and MEM should be accurate no matter where it runs.

(10-06-2016 02:22 PM)Bruno Wrote:  Successives call of MEM : (After each call, from 5 to 9 bytes of memory are used, I don't know why at the moment)

Because every time you call MEM it allocates memory for the BINT you are pushing, which takes 12 bytes. Even if you clear the stack, the UNDO feature keeps the last 8 stacks (and uses a couple of extra words too to mark the snapshots), so the GC won't collect that memory. If you run it more than 8 times, the number should remain constant, as all 8 stacks would contain a single BINT.

(10-06-2016 02:22 PM)Bruno Wrote:  PART 2 - VERSION

I would like to implement the VERSION command, and as a simple demo I try this code :
Code:
    case VERSION:     {         char version[] = "newRPL alpha0.6.01";         char copyright[] = "(c) Claudio Lapilli";         WORDPTR versobj = rplCreateString((BYTEPTR)version, (BYTEPTR)version + stringlen(version));         if(!versobj) return;         WORDPTR copyobj = rplCreateString((BYTEPTR)copyright, (BYTEPTR)copyright + stringlen(copyright));         if(!copyobj) return;         rplPushData(versobj);         rplPushData(copyobj);         return;     }

but I was unable to compile the ROM because gcc seems to do an implicit call to 'memcpy' that the linker was unable to resolve :
Code:
lib-65-system.o: In function lib65_handler': lib-65-system.c:(.text+0x4e0): undefined reference to memcpy' Makefile:497: recipe for target 'newrplfw.elf' failed collect2: error: ld returned 1 exit status

(My guess about the origin of the memcpy is the copy of the string from the ROM to the RAM, but I'm not sure)

Avoid that like the plague. We don't want any compiler built-ins bloating the ROM.
The offending code is the allocation of the constant strings.

char *string = "Hello";

This allocates a write-able pointer called string, pointing to a write-able string pre-initialized as "Hello". The key here is that because we are running from ROM, the compiler knows that it needs to allocate space in RAM and copy the string there at run time.
To avoid it you need to use 'const', and actually a weird combination of double const for embedded systems:

const char const *string = "Hello";

This allocates a read-only pointer called string (const *), which points to a read-only string (const char). But the problem is in other platforms (PC and friends) the compilers don't like the double const statements and give warnings.

Anyway, the proper way to do it would be to put the strings in a ROMOBJECT, and add a .nrpl file to the project, look at how libraries incorporate RPL code in them by looking at how they link to those .nrpl files.
Static strings are a big NO in newRPL because most messages should be able to be translated without having to scan the code for strings.
It's quite simple to declare ROMOBJECTs that reside in an external RPL file. This RPL file should contain those 2 strings, which your handler simply needs to push to the stack. This way we can update that RPL file with the new version string without having to change the actual code, and if it's a separate RPL file containing exclusively those 2 strings, a build script could easily spit out build numbers automatically.
 « Next Oldest | Next Newest »