I've had a lot of fun and a lot of frustration in the last couple of weeks working with my GRAM-KRACKER, working with very fragmentary information about GPL, trying to make my 'custom' machine do what I wanted it to do. In the course of all this I've found out a few things and picked up a couple of tips that may interest some of you. I am not claiming that my techniques are the best, my code the most compact or efficient, or my knowledge particularly profound. In fact, the opposite of all that is probably more accurate picture of where I'm at. On the other hand, these notes may save somebody else some of the frustration I went through, and thereby make for more _fun_, which is what this deal is all about!
I got an 80K G/K, mainly because I wanted to load custom character sets in GROM 0, and store E/A and TIW in GROMs 1 and 2. Loading character sets is no big deal, once you discover that the utility provided with the GK only copies the last seven bytes each character definition and that the operating system only holds seven bytes for each 'small' character, using them to define the _last_ seven bytes of each eight-byte definition. Anyway, once I had the characters loaded I wanted to be able to get at them all easily. This required adding two new calls: one to load the so-called 'title screen' characters (upper case only) and one to restore lower case (since the built-in CHARSET already restores the upper case.) The code for these calls was already available in the operating system, spelled out in the June '84 issue of SMART PROGRAMMER. The problem I ran into, however, was figuring out th proper method to RETURN from the CALL so that I would re-enter X-BASIC cleanly, and that the CALL would work in a running program.
Understand that it is _easy_ to program directly in GPL _without_ an assembler, as long as you are writing short, simple CALLS which use no symbolic or relative references. Commands translate fairly directly into bytes, and GPL is compact enough so that you can literally read it direcly, once you get the hang of it.
From various sources, The EXPLORER manual, SMART PROGRAMMER, and especially the excellent and fairly complete manual for the Pascal GPL Assembler in DL4, it seemed that the simple byte command >00 was a RETURN and, sure enough, using >00 seemed at first to work. It always worked in command mode and sometimes worked from running programs. But obviously sometimes is not good enough. From checking out other calls in X-BASIC and in the XBCALLS GK utility, I finally realized that RETURN itself needed to be called. CALL in GPL is >06, and CALL must be followed by a _vector_ for the called routine. That is, an address containing the address of the routine. The EXPLORER manual tells all, including the address of a routine which is suppossed to be "Return from LINK or DSR." Well it also works as a return from CALL, so it seems. Its address is at >0012 in GROM 0, so the complete RETURN fragment in GPL turns out to be:
>06 CALL >00,>12 GPLLNK VECTOR FOR RETURN
Now that may not be the best way to return from a simple CALL, but it has an obvious advantage: it works. So does another fragment used by some of the simple calls in XBCALLS:
>05 BRANCH >C1,>E3 (an address in the XBasic GROM which apparently also CALLS the correct RETURN)
So add the return to the Load Title Screen Characters code listed in the June *SP* and you get:
>31 GROM to VDP MOVE >02,>00 # of bytes to move >A9,>00 VDP DESTINATION ADDR plus offset >04,>B4 GROM SOURCE ADDRESS >06 CALL >00,>12 RETURN VECTOR
Unfortunately, this moves the definitions to the wrong address for BASIC, because of the notorious BASIC offset. But alter the VDP DESTINATION adress and it works just fine! Another approach is to CALL the Load Title Screen routine provided for use by GPLLNK. This is a a _different_ routine from the code above, and it uses a vectored destination address. (You remember, place the destination address at FAC...)
>BF CPU STORE >A4 at >83A4 (addresses in PAD are referenced by LSBy) >04,>00 VDP DESTINATION without offset >06 CALL >00,>16 GPLLNK VECTOR for LOAD TITLE SCREEN CHARACTERS ROUTINE >06 CALL >00,>12 RETURN
Both versions are 10 bytes long; both work.
Of course, when you add a CALL you have to go to the last 'SUBROUTINE HEADER' or what I call the last entry in the CALL ref/def table, and add the header for the new CALL. You have to modify the previous header to point to the addre ss of the new header; give the address of the next header (or >00,>00 if there are no more); give the length of the CALL name and the name itself; and finally give its address. Where to put the code? Anywhere you want, but consider MG's XBCALLS. They continue the chain of CALL headers starting at G>D800, but scatter their code through the remaining GROM space. I found that the code for CLSALL and CLKOFF, both short six-byte sequences with no relative references, are stuck out in the middle of nowhere, so I moved them to G >DA58 (right after CLOCK) and changed their headers. This left me lots of room to add my new CALL headers. I then tacked the code for my CALLS after CLSALL and CLKOFF, at G>DA64 and beyond. [The long blocks of code that constitute CLOCK and CAT contain relative references, symbols and the like, and are too messy to try to move. One tip here though; don't overwrite the >00,>00 you find at G>DEC3. These bytes are part of the CAT code and must be left blank. Add anything you want at >DE40 and beyond.]
Once I got my CALL BGCHAR working to load my custom Title Screen character set, I added CALL LC to restore the lower case character set to 'normal;' very useful for chaining programs:
>BF CPU STORE >A4 at >83A4 >06,>00 VDP DESTINATION without offset >06 CALL >00,>4A GPLLNK VECTOR for LOAD LOWER CASE CHARACTERS >06 CALL >00,>12 RETURN
Using these built in GPLLNKs and CALLing other CALLS is so _easy_, letting the GPL intrpreter keep track of returns, that I went on and added CALL BEEP and CALL HONK:
>06 CALL >00,>34 VECTOR for BEEP >06 CALL >00,>12 VECTOR for RETURN >06 CALL >00,>36 VECTOR for HONK >06 CALL >00,>12 VECTOR for RETURN
And finally I checked out the actual GPL code for HONK and decided to try to add CALL CHIME. I had to store the data for the CHIME sound list somewhere (the data is in the E/A manual) so I tucked it down at G>DE40. The code for the CALL is as follows:
>BF CPU STORE >58 at >8358 >DE,>40 address of my sound list >F6 INPUT/OUTPUT INSTRUCTION >58 CPU SOURCE address vector >00 I/O FLAG: defines this as a SOUND operation with the sound list in GROM >06 CALL >00,>12 VECTOR for RETURN
This code turns on the sound and immediately returns to the next GPL statement. The E/A manual includes >8358 in a general area defined as a stack area, used by DSR's, etc. I don't know if any other PAD address would work as well.
Once I got all this stuff going, I decided to go for the big one. I had previously written a package of text-mode utilities for X-BASIC [see X40 in DL4] and I decided that I could store the code for those utilities in the remaining unused X-BASIC GROM space and write a simple CALL to read them into memory. I mean, SYSTEX is great, but memory-to-memory is the ultimate.
First I had to refine the X40 code a bit. I already had my alternate character set in console GROM, so I took out the ALCHAR routine and its PAB and a little associated baggage. Then I moved all the buffers to the _end_ of the source code so all the real meat of the code would load contiguously at the top of low memory. I wanted to be able to fit everything I needed into the unused space in GROMS 3,4, and 5, leaving the rest of GROM 6 free for more extensions of X-BASIC and GROM 7 free altogether.
I had >17F0 blank bytes of GROM available; all I needed to save was my program code and the ref/def table. My code took up the space between >24F4 (the First Free Address after CALL INIT) and about >3870, the start of the buffer space. This was only >137C of code; the ref/def table was another >A0 bytes, still plenty of room. But how to get this code, with all its symbolic and relative addresses, from low memory into GROM? True, I only had to do this once, but I certainly didn't want to have to type it all in!
I couldn't use the GK editor for the move, because _it_ resides in lower memory, and overwrites the code I needed to move. So I took a round-about route. I wrote a temporary GPL CALL to move the entire 8K low-memory into high memory from X-BASIC (after CALL INIT::CALL LOAD(DSK1.X40:OBJ"). Then I used the GK editor to move the code from high-memory into GROMs 3, 4, and 5. Here's the code that moved the stuff from low to high:
>35 CPU MOVE >20,>00 BYTE COUNT >8F CPU >1D,>00 DESTINATION ( ->8300 OFFSET) >8F CPU >9D,>00 SOURCE (MINUS OFFSET) >06,>00,>12 THE USUAL RETURN
This is lifted of course straight from CM's tutorial on GPL moves.
I moved the code into GROM in three chunks, >7F0 bytes to G>7810 (>7800-7809 used by the defeat auto-load patch), >800 bytes to G>9800, the remaining bytes to G>B800 with the ref/def table moved to G>BB90. This was very easy with the GK editor. I immediately Saved the module with the A/L code in place just in case I screwed up the next step. Then I wrote the GPL CALL to move the code back into place in low memory.
Friend Miller does advise that it's faster to write an A/L mover, use GPL to move it into place in low memory, then branch to it and let it move out the rest of the code. This kind of boot-strapping is said to be faster than using GPL to move the whole works directly; it's certainly more elegant. GPL is still simpler, however, and the speed is fast enough with memories of loading this stuff from disk so fresh in my mind. So, four GPL move instuctions gets the three code blocks and the ref/def table back into place, and one last move instruction moves out new First Free Address and Last Free Address values to >2002. It may be driving a nail with a sledge-hammer, but it works! Here's the actual code:
>31 GROM to CPU move >07,>F0 BYTE COUNT >8F MARKS CPU ADDRESS >A1,>F4 DESTINATION, CPU >24F4 MINUS >8300 OFFSET >78,>10 GROM SOURCE
And so on...
>31,>08,>00,>8F,>A9,>E4,>98,>00 >31,>03,>86,>8F,>B1,>E4,>B8,>00 >31,>00,>B0,>8F,>BC,>50,>BB,>90 >31,>00,>04,>8F,>9D,>02,>DA,>B8 >06,>00,>12
Now from X-Basic I issue CALL INIT :: CALL X40 and I am ready to use text mode! Who need MYARC!
I hope these code fragments and procedural notes do serve a useful purpose. I am glad we are all respecting TI's copyright and not uploading actual altered modules, but it does make for more emphasis on technique in the message base and the DL's.
Once more written material on GPL is available this will all be a lot easier, I am sure, and my examples will be seen for the clumsy dross they probably are. Personally, I look forward to having a nice manual and not having to read through the GRAM KRACKER manual, the EXPLORER manual, SMART PROGRAMMER, and various other sources, feeling like a biblical scholar trying to pin down specific references and cross-reference ambiguities. But then again, I guess that's why they call us KRACKER-HACKERS!
Back to main TI Page
Back to TI Download Page
This is FABbnet!
Document maintained at the Fine-Arts Bluesband, © 2003. Direct comments to rgm at fabbnet.net.