Philip I. Nelson, Assistant Editor
In Search Of The Shortest ST Program
The tale behind this month's program began when I undertook to write a desk accessory program for the ST. Desk accessories are newly installed when the computer does a cold start—when you turn the power off and on—but not when you press the reset button. To test each new version of the accessory, I found myself turning the power off and on, over and over. Computers are sturdy tools, but flipping the power switch, say, 40 times in an evening isn't particularly good for any electrical device, be it a microcomputer or a dishwasher. To make life easier on me and my ST, I wrote a program that causes a cold start.
Short and Simple
The resulting program is only 59 bytes long—one of the shortest ST programs you're likely to see. Even if you're not writing an accessory, there are many situations when it's useful to reset the ST to virtual power-on status. Some crashes can leave the computer in apparent working order when it's actually confused about how much memory is free, how many files are open, and so forth. Another problem has to do with memory allocation. If you press the reset button while a RAM-disk is present, for instance, the RAMdisk becomes unusable, but the memory which it uses may not be released to the system. Here's the assembly language source code for this program in its entirety:start:
The first three instructions shift the processor from user mode into supervisor mode so that we can access otherwise forbidden addresses such as system variables.
This is done by executing GEMDOS routine $20, known as Super. In 68000 assembly language, system routines are executed through a trap instruction. A trap #1 instruction executes a GEMDOS routine, trap #14 executes an XBIOS routine, and so forth. Most system routines expect to receive information of one sort or another; such information is passed by pushing it onto the processor's stack before you execute the trap. In this case, only one parameter is needed: a zero to signal that we wish to go from user mode to supervisor mode.
Once the parameters have been pushed, you must push the opcode that identifies the routine ($20, in this case). After returning from the trap, you would ordinarily increment the stack pointer to adjust for the bytes that were previously pushed (after a call to Super, you would perform addq.l#6,sp to adjust for a four-byte longword and a two-byte word). But that's wasted effort in this case, since we know that a cold start causes the ST to reinitialize its stack pointer, anyway.
Most routines return information, as well, typically in register d0. Super returns the previous address of the supervisor stack pointer. Under ordinary circumstances, this address should be saved so that you can switch back to user mode when you've finished working in supervisor mode. Since we don't expect to return from a cold start, the program ignores this otherwise critical information.
The fourth instruction clears the system variable memvalid($420) to indicate that the current memory configuration is no longer valid. This forces the ST to clear and reconfigure its memory, steps it might otherwise skip on reset. The last two instructions move a 32-bit address from location $4 into address register a0 and perform an indirect jump to that address. Location $4 contains the address of the code to execute on reset: To emulate a cold start, we simply jump to the same code the computer executes when you turn on the power. The same address is usually found in sysbase($4f2), the system pointer to the beginning of the operating system.
GEM Loader Oddity
You may wonder why the source code begins with a useless label (start:) and ends with an equally useless constant definition (.dc.l start) The answer concerns the GEMDOS loader that loads and runs ST programs. If you assemble this program without the label and constant, GEMDOS refuses to run it, generating the message TOS error #35 about nine times out of ten. That message appears when you try to run something (often, a garbled or misnamed file) which GEMDOS doesn't recognize as an executable program. Since this program is executable, why doesn't it run consistently without the extra baggage?
Begin with the facts that all ST programs are expected to be relocatable, and that GEMDOS ordinarily decides where to load a program based on the ST's current memory configuration and the program's memory requirements. In addition to the naked code itself, each ST program file begins with a short header that tells GEMDOS what it needs to know for loading. Most programs refer to variables or data of some sort, and one of the loader's more important tasks is to resolve such references after it has brought the program into memory. At the very end of the program file is relocation data that tells the loader which program elements to adjust.
This program is highly unusual in that it doesn't need to refer to any data or variables whatsoever. Paradoxically, it's that very simplicity that causes GEMDOS to balk at this program unless we include a useless reference. The loader seems to need at least one reference that requires relocation. If no such reference exists, the loader takes relocation information from free memory and gets confused more often than not. Thanks to COMPUTE! programmer Tim Victor for tracking down this ST feature, which you may or may not consider a bug. (It's a rare program, after all, that can get by without any external variables or data.)
Of course, no program—especially one this short—can eliminate the need for an occasional hardware reset. There are still many cases when the only prudent solution is to reach for the reset button or the power switch. For those who don't have an assembler, here's a BASIC filemaker that creates the program under the name COLD-STAR.PRG:
100 close:open "R",1,"A:\COLD STAR.PRG",59
110 field #1,59 as a$
120 for j=l to 59:read byt$
160 1set a$=x$:put 1,0:close
170 if chk<>3207 then ? "Typing error."
180 data 60,lA,00,00,00,1A,00
190 data 00,00,00,00,00,00,00
200 data 00,00,00,00,00,00,00
210 data 00,00,00,00,00,00,00
220 data 42,A7,3F,3C,00,20,4E
230 data 41,42,B9,00,00,04,26
240 data 20,79,00,00,00,04,4E
250 data D0,00,00,00,00,00,00
260 data 00,16,00
COMPUTE! magazine is currently looking for quality articles on Commodore, Atari, Apple, and IBM computers (including the Commodore Amiga and Atari ST). If you have an , interesting home application, educational program, programming utility, or game, submit it to COMPUTE!, P.O. Box 5406, Greensboro, NC 27403. Or write for a copy of our "Writer's Guidelines."
All versions of this handy utility program from the September issue (p. 52) suffer from the same minor bug. If the directory contains an odd number of filenames, the last name in the alphabetized list will not be printed on the jacket. (This occurs only for odd numbers greater than 32 in the Commodore and Apple versions.) To correct this, make the appropriate change for the version of the program you are using.
For Commodore (Program 1), Apple (Program 3), and IBM (Program 5), change line 870 to:
870 CX = INT((C - 33) / 2+0.5): CZ = CX+32
For the IBM only, also add the following line:
445 C = C+1
The IBM version has an additional limitation. Because of the way directory entries are read from the disk, the maximum number of entries that can be read is 76. For disks that contain more than 76 files, only the last 76 filenames will be printed on the jacket. Thus, the test for more than 88 filenames in line 190 is irrelevant.
For Atari (Program 2), add line 305 and change line 530 as shown:
305 DIR$(FILE*17+1,LEN(DI R$))="(17 SPACES)":FILE=FILE+1
Amiga Pyramid Power
We stated in last month's CAPUTE! column that we'd try to avoid the use of lowercase 1 as a variable name in Amiga program listings, since that character is impossible to distinguish from the number 1 in our listings. Unfortunately, at that the time the October issue containing the "Pyramid Power" was already out, and it has the same problem. The problem is amplified in Pyramid Power because that program uses both k1 and k1 as variable names, and it's impossible to tell which is which. In the following cases, the variable should be k1 (we recommend using KL to make the change more obvious):
In the rightmost column of page 57, both instances of IF KL=1 THEN gameover.
In the leftmost column of page 58, both instances of IF KL=1 THEN gameover.
In the move: subroutine, both instances of IF KL = 1 THEN RETURN.
In the creaturerock: subroutine, NEXT: KL=1: RETURN.
In the edge: subroutine, IF z>6 THEN KL=1: RETURN.
In the gameover: subroutine, score = 0: lev = 1: sq = 0: ts = 0: sp=.25: KL = 0: GOTO readdata.
In any other cases, the variable listed should be k1.
In the following cases, the variable should be 1 (we recommend using L to make the change more obvious):
In both the player: and creature-shape: subroutines, L=87:DIM a(L): and FOR i=0 TO L: