ZFlash from Joe Pemberton

Ok today I will review zflash from Joe Pemberton.

As always, I could say somethign wrong, I'm not an asm guru, I simply try to understand source code in the same time I write this review.

What's zflash?

This is an tool to archive/unarchive a program directly from a running basic program.

How to use it?

You need to store the program name into ANS (last operation result).
To tell zflash if you want to archive or unarchive the prog, you need to prefix it by a star or an empty space.

Look at the readme it's probably a better explanation than mine :

ZFlash is a simple program that many BASIC programmers have been
requesting for a long time.  This program will allow BASIC
programmers to archive and unarchive programs from WITHIN their
programs.  It is simple, fast and compact.
To use ZFlash, the syntax is simple.  First, you must define if
you want to ARCHIVE a program or UNARCHIVE.  Do this by storing
a space " " for UNARCHIVING or a star "*" for ARCHIVING in ANS.

to archive:
:"*"
to unarchive:
:" "

Next you must define which program you wish to archive/unarchive.
Do this by adding the name of the program onto the "*" or " ".  So
say you want to unarchive program "HELLO".  To define this, you would do
:" HELLO
Or if you wanted to archive program "ZGAME" you would define it like this
:"*ZGAME
Then immediatly run prgmZFLASH with the syntax
:Asm(prgmZFLASH

Unarchive program TEST:
  :" TEST
  :Asm(prgmZFLASH
And prgmTEST is unarchived.

Archive program MATH:
  :"*MATH
  :Asm(prgmZFLASH


Don't waste time and let's start to see the source code !

.nolist #include "include.inc" .list .org ProgStart83p B_CALL(_rclans) ;returns de as a pointer to the string ex de,hl ;hl=pointer to string ld c,(hl) ;c=low byte of length inc hl ;high byte of length.. ld b,(hl) ;b=high byte of length inc hl ;now we're at the (un)archive define " /*" ld a,(hl) ;a='*' for archiving, ' ' for unarchiving


Ok so at the start we have the usual .nolist, the include (include.inc could probably be replaced by ti83plus.inc).

Then .list say that declarations are finished and we start real job :)
The .org ProgStart83p should probably be replaced by .org 9D93h as this program is for ti83plus/ti84plus (ti83 regular hasn't flash memory...).

To start, Joe Pemberton needs to retrieve the string into ANS, this is done by calling B_CALL(_rclans) which will put the adress of the string into de.
After that he exchange (ex) hl by de and get the length of the string into bc.
Seems like the two first bytes given by _rclans simply define the length...
Then we start looking at the string which is something like *PROG or PROG (with a space at the start of the string.

So Joe increments hl and get the space or the start into a.

At this point, we have the size of the string, the type of action to do, we need to get the filename of the prog and perform the task !

push af ;save a inc hl ;start of name data push hl ;save hl for _zeroOp1 B_CALL(_zeroOp1) ;set Op1 to zeros pop hl ;restore hl ld a,5 ;5 for program definition ld (op1),a ;load it into Op1 ld de,op1+1 ;de=pointer to op1+1 ldir ;copy name of prog to the rest of Op1 B_CALL(_chkfindsym) ;look it up (returns archive status in b) jr c,quit ;if carry is set, program wasn't found so quit pop af ;restore a - a now equals the " /*" flag cp tmul ;compare it to '*' jr z,archive ;if it is '*', archive the program


So the register a is saved, and hl is incremented and points now to (the start of) the name of the program.
Then Joe calls the zeroOp1 to set Op1 to... ? Zero of course !
This call destroy hl so Joe push the hl on the stack to protect it then get it by pop hl after.
What's Op1? This is a place in memory which is used by the calc to do operations which could not fit into the small registers (even 16 bits is small).

Then Joe put 5 into Op1.
He set de to the next byte of op1 and use ldir to copy the content of hl into de.
ldir simply copy (hl) into (de) then inc hl and inc de until bc is equal to 0...
Remember that bc contains the length of the program name.

To find a var on your calc, you need to use _chkfindsym (and having put the name into op1 with the type in the first byte etc...).
So Joe calls this and test if the progs was found (carry is set if prog is not found).
Then Joe retrieve the star or empty space to test which kind of task he needs to perform on the program.
If the value of a is the star (tmul is an equate from an include file), then jump to archive.

cp tspace ;compare it to ' ' ret nz ;if it isn't ' ', quit ld a,b ;load archive status of program into a or a ; ret z ;if it is zero,program is already unarchived so quit jr done ;unarchive the prog archive: ; ld a,b ;a=archive status or a ; ret nz ;if it isn't zero, then the program is already archived so quit done: B_CALL(_arc_unarc) ;archive (or unarchive) the program, quit push af quit: pop af ret ;quit .end END


Ok if the first char is an empty space, then continue (ret only if different from star and empty space).
It seems that after chkfindsym, b contains the status of the prog (archived or not).
So if the program is already unarchived (status = b = 0) do nothing (ret z).
Else, jump to done (jr is like jp but faster and smaller in memory (but could only do small jumps).
(notice that djnz do a jr too)

In archivei label, if the prog is archived (status != 0) then do nothing (ret nz).
Notice that doing or a is better than testing with cp (compare). This is faster and smaller too.
Then finally the B_CALL(_arc_unarc) will archive/unarchive the prog depending the value stored in a I guess(?!).
I was looking for the details about this call but I can't find the information I need quickly... So I let you search it :D

I hope you learned something with this review.
As you can see, this is not really hard to deal with vars and archiving/unarchiving is an easy task.
Now you're ready for moving from flash memory to ram and vice versa.