High Score writeable ATR example in BASIC - FujiNetWIFI/fujinet-firmware GitHub Wiki

So you want to experiment with FujiNet's https://github.com/FujiNetWIFI/fujinet-platformio/wiki/High-Score-storage-for-Legacy-Games? Here's how you can experiment with it on an Atari with BASIC.

A hex editor (e.g., hexedit on Linux) is required, though you could also be clever with tools like dd to automate modification of ATR files (e.g., as part of a Makefile build process). Determining how to find the sector of a file (e.g., on an Atari DOS or MyDOS formatted disk image) from a host system like Linux is left as an exercise to the reader. (Once you've figured it out, please update this wiki page to explain the process!)

A simple program

  • Create a new blank disk image. (You can do this directly on your FujiNet. Navigate to "SD", and press [N] to create a new disk image.)
  • Boot into your favorite DOS, format the new disk image (thus creating a filesystem), and write DOS to it (thus making it bootable).
  • Boot the Atari into BASIC, with the new disk image mounted on drive 1 (D1:) in READ/WRITE mode.
  • Input this program and save it to the disk image:
0 REM SAVE"D:WRITER.BAS":RUN
5 TRAP 50
10 DIM A$(128)
20 OPEN #1,4,0,"D:WRITABLE.DAT"
30 INPUT #1,A$
40 ? "CURRENT TEXT:":? A$
50 CLOSE #1:TRAP 100
60 OPEN #1,8,0,"D:WRITABLE.DAT"
70 ? "ENTER TEXT"
80 INPUT A$
90 ? #1;A$
100 CLOSE #1

Testing writable vs not-writable

RUN the program, and enter some text (e.g., "HELLO, WORLD"). It will be saved in the text file WRITABLE.DAT. If you RUN again, you'll see your text (and be prompted to change it; hit [Break] to abort).

You may now try rebooting, but with the disk image mounted in READ-ONLY mode. RUN the program; you will see the text you entered before, but you cannot change it. (The OPEN-for-write (mode 8) command on line 60 will fail, and the error is TRAP-ed to line 100, where the program ends.)

Finding the sector

In BASIC, you can issue the following commands to determine the sector of the WRITABLE.DAT data file:

OPEN #1,4,0,"D:WRITABLE.DAT"
NOTE #1,SECT,BYT
? SECT

Updating the ATR

Say for example, the data file ended up on sector 94.

Use a hex editor to change bytes 12, 13, and 14 (0x000C, 0x000D, and 0x000E) of the ATR disk image file. Place 0x01 in position 12, representing the fact that only one sector will be writable. And place 0x5E and 0x00 in positions 13 and 14, representing the low byte and high byte of the sector that should be writable.

Try it out

Reboot with the modified disk image file, again in READ-ONLY mode. Now, when you RUN it, you should be able to modify the contents of the data file (and only the data file).

If you watch the serial output logs of your FujiNet over the USB cable, you'll see this following the "ATR WRTIE" log line: "High score mode activated, attempting write open". (At least, per 2022-10-12's 0.5.7eb55adb firmware version.)

An example Hi-Score screen

Shown here, an algorithm that takes the score, as contained in CASH, and compares it against the other scores stored on disk, scooting the others out of the way, if needed, to make room for a new score. It then copies the score, and gives the opportunity to enter initials, after which, the scores file is stored.

Line 3000 starts a sub-program that can create a new scores file.

This code uses the screen codes, as stored in memory, and does the comparisons directly to screen memory, so it's very fast.

2000 REM HIGH SCORES
2001 POKE 756,CHSET/256:SETCOLOR 4,6,2:SETCOLOR 0,1,10:SETCOLOR 3,4,12:SETCOLOR 2,3,6
2010 OPEN #1,4,0,"D:SCORES"
2020 SCRN=PEEK(89)*256+PEEK(88)
2030 FOR X=SCRN TO SCRN+479
2040 GET #1,A:POKE X,A:NEXT X
2050 CLOSE #1
2060 POSITION 0,23:? #6;"CASH: ";
2061 IF INT(CASH/100000)<1 THEN ? #6;" ";
2062 IF INT(CASH/10000)<1 THEN ? #6;" ";
2063 IF INT(CASH/1000)<1 THEN ? #6;" ";
2064 IF INT(CASH/100)<1 THEN ? #6;" ";
2065 IF INT(CASH/10)<1 THEN ? #6;" ";
2066 ? #6;CASH;
2070 REM FIND SCORE SLOT
2080 FOR X=0 TO 9
2081 O=X*20+40+9
2090 FOR Y=0 TO 5
2100 B=PEEK(SCRN+466+Y)
2110 A=PEEK(SCRN+O+Y)
2120 IF A>B THEN POP :NEXT X
2121 IF B>A THEN S=X:POP :POP :GOTO 2160
2130 NEXT Y
2140 NEXT X
2150 GOTO 2330
2160 IF S=10 THEN 2200
2170 FOR X=9 TO S STEP -1
2171 S1=X*20+4+40:S2=(X+1)*20+4+40
2172 FOR Y=0 TO 10
2173 POKE SCRN+S2+Y,PEEK(SCRN+S1+Y)
2174 NEXT Y:NEXT X
2180 S1=S*20+4+40
2190 FOR Y=0 TO 10
2191 POKE SCRN+S1+Y,0
2192 NEXT Y
2193 POKE SCRN+S1,14:POKE SCRN+S1+1,14:POKE SCRN+S1+2,14
2194 S1=S*20+9+40
2195 FOR X=0 TO 5
2196 POKE SCRN+S1+X,PEEK(SCRN+466+X)
2197 NEXT X
2200 OPEN #1,4,0,"K:"
2201 S1=S*20+4+40
2210 FOR X=0 TO 2
2220 GET #1,K
2230 IF K=126 THEN X=X-1:NEXT X
2240 POKE SCRN+S1+X,K-32
2250 NEXT X
2260 CLOSE #1
2300 OPEN #1,8,0,"D:SCORES"
2310 FOR X=SCRN TO SCRN+479:PUT #1,PEEK(X):NEXT X
2320 CLOSE #1
2330 POSITION 0,23:? #6;"PRESS ANY KEY":OPEN #1,4,0,"K:":GET #1,K:CLOSE #1:RUN 
3000 REM REINITIALIZE HIGH SCORES
3010 GRAPHICS 17:? #6;"    TOP EARNERS   ":? #6;""
3020 FOR X=1 TO 10
3030 IF X<10 THEN ? #6;" ";
3040 ? #6;X;"."
3050 NEXT X
3060 OPEN #1,8,0,"D:SCORES"
3070 SCRN=PEEK(89)*256+PEEK(88)
3080 FOR X=SCRN TO SCRN+479
3090 PUT #1,PEEK(X)
3100 NEXT X
3110 CLOSE #1
3120 END