Modification and Relocation of FOCAL 65-E Into Erasable PROM
William C. Clements, Jr.
Dept. of Chemical & Metallurgical Engineering
The University of Alabama
P.O. Box 2662
University, Alabama 35486
After using FOCAL for awhile, I became interested in storing the machine code in EPROM. Not only would this eliminate much of the waiting for tapes to load, but more important, it would free over 5K of user RAM for other purposes such as storing more FOCAL source code and variables, or for graphics routines.
The relocation of FOCAL and execution of it from PROM is not as straightforward as for some other programs, because the machine code is self-modifying in several places. Also, there are multitudes of data bytes used for address pointers scattered through the program, and these are in such a form that the ordinary kind of relocation routine would not convert them. Thanks to the excellent documentation supplied with FOCAL, I was successful in relocating it in a “clean” non self-modifying form. The code, together with an initialization routine that sets up page zero and other RAM locations used for user statements and to make the code “clean,” fits neatly into three 2716's with plenty of room left over for other modifications such as tape load and save, “user” function, etc., which I have added to my version of FOCAL as well1. The modifications which follow are concerned with cleaning the code up for storage in PROM, and pertain to FOCAL 65-E for the KIM-1, obtained from the 6502 Program Exchange in Reno, Nevada.
The first order of business in preparing FOCAL for PROM is to get rid of the self-modifying parts. The three places I found where FOCAL modifies itself in the main code are at locations $2348-2353, $282C-283D, and $3408-3414. A fourth place occurs in page zero, where it doesn't matter since page zero is always RAM in 6502 systems. The other places are easily fixed. I borrowed a few locations from an obscure corner of KIM's on-board RAM to do it; neither KIM nor FOCAL seems to mind. The changes are as follows:
2348 was 8C 52 23 change to 8C DE 17 STY DJADR
234E was 8C 53 23 change to 8C DF 17 STY DJADR+1
2351 was 4C 00 00 change to 4C DD 17 JMP $17DD
282C was 8C 3C 28 change to 8C DB 17 STY DJADR1
2835 was 8D 3D 28 change to 8D DC 17 STA DJADR1+1
283B was 6C 00 00 change to 4C DA 17 JMP $17DA
3408 was 8E 12 34 change to 8E E1 17 STX MOV11
340C was 8C 14 34 change to 8C E3 17 STY MOV22+1
3411 was B5 00 change to 4C E0 17 JMP MOVIT
3413 was 95 00 change to EA NOP
Additional code needed in page 17 is:
17DA 6C 00 00 JMP (0000)
17DD 4C 00 00 JMP 0000
17E0 B5 00 MOVIT LDA(X) 00
17E2 95 00 STA(X) 00
17E4 4C 15 34 JMP 3415
The address overwriting now occurs in page 17 RAM instead of in the main code, which can now be safely put into PROM.
Before doing so, however, we must relocate it. Note that relocation should not alter existing page boundaries (see warning on p. 44 of FOCAL 65-E Manual). This actually makes the job easier, because only the high-order bytes of addresses and address-words can be changed. Relocation then is accomplished by (a) adding the desired offset to the third byte of all three-byte instructions which do not reference page zero; (b) Offsetting the data words for routines such as PUSHJ and POPJ, the software stack manipulators. These words are scattered here and there through all the code. A listing of their high-order halves is given in Table 1; they are address words, so only the second byte is to be offset. (c) Offsetting the high-order bytes of the address tables at the end of the FOCAL code, which are at hex locations 34FA-3515, 3546-3557, 356A-356E, 3598-359C, 35A2-35A6, 35AC-35B0, 35B6-35BA, 35C0-35C4, and 35CA-35CE. (d) Adding the offset to the IRQ-vector initialization byte at location 34AE (I dare your cleverest relocation program to find that one!).
A final change necessary to execute FOCAL from PROM is to change the RAM allocation for program statements and variables so it is located in RAM, instead of at the end of the machine code to go in PROM. The original start of this allocation is at location 3533, but if you are going to PROM your FOCAL I suggest you save some PROM locations by deleting the heading that is printed as if it were line number 00.00 by the Write command. I retained only the line number zero and a carriage return in my version, since the program expects to print something there. This saves twenty-seven bytes of memory. In my system, I decided to start the RAM storage for statements and data at location 4000, so initialization there is as follows:
4000 00 ;line number
4001 00 ;of 00.00
4002 OD ;ASCII 'CR'
4003 FE ;PBEG
4004 FF ;VEND
To tell FOCAL where to put its statements and variables, some page zero locations need to be changed:
002F was D4 35 change to 00 40 ;beginning of RAM allocation
0031 was F2 35 change to 03 40 ;start of user's text
003E was F3 35 change to 04 40 ;start of variable list
0040 was F3 35 change to 04 40 ;start of variables for "erase all"
0042 was F3 35 change to 04 40 ;end of variable list
The code to accomplish page zero and page 17 setup and initialize the user RAM is given in Table 3. The code begins at location 3677 instead of right after the FOCAL code because I have some other modifications in between; the user will want to relocate this to suit his system anyhow.
1 See 6502 User Notes, issue #16, and errata in issue #17.
Table 1.
Table of High-Order Data Bytes Used by POPJ and PUSHJ. Add Offset to relocate.
Hex Location |
Original Contents |
Hex Location |
Original Contents |
Hex Location |
Original Contents |
Hex Location |
Original Contents |
2088 | 23 | 24BB | 29 | 29E5 | 2D | 300D | 2B |
20B2 | 23 | 2502 | 2B | 2A45 | 2B | 309E | 29 |
20D7 | 29 | 2516 | 29 | 2A5D | 29 | 316A | 2B |
212F | 21 | 2533 | 29 | 2ABE | 29 | 3186 | 29 |
219E | 21 | 2546 | 29 | 2B97 | 29 | 31A8 | 21 |
21D0 | 23 | 256A | 29 | 2EFF | 29 | 34AE | 2C |
21FE | 23 | 257A | 23 | 2F7F | 29 | ||
2440 | 2B | 25EB | 29 | 2FA3 | 29 | ||
2452 | 29 | 29DC | 29 | 2FE8 | 29 |
Table 2. FOCAL Initialization
3677 A2 00 COLDST LDX $00 ;Initialize table & instructions BD A0 36 LOOP1 LDA(X) TABL1 ;at page zero 95 20 STA(X) E8 INX E0 BD CPX $BD ;Initialize $17BA-$17E6 for D0 F6 BNE LOOP1 ;removal of self-modifying code A2 00 LDX $00 ;in FOCAL BD 5D 37 LOOP2 LDA(X) TABL2 9D DA 17 STA(X) $17DA E8 INX E0 0D CPX $0D D0 F5 BNE LOOP2 ;Initialize User RAM 3690 A2 00 LDX $00 ;with line number BD 6A 37 LOOP3 LDA(X) TABL3 ;zero and data bytes 9D 00 40 STA(X) $4000 E8 INX E0 05 CPX $05 D0 F5 BNE LOOP3 ;Go to FOCAL cold start 369D 4C 00 20 JMP FOCAL ;page zero constants & code 36A0 {contents of} : {FOCAL locs.} TABL1 : {$0020-$00DC} : {go here. } ;Table for patches to 375C ;remove self-modifying 375D 6C 00 00 TABL2 ;code in FOCAL 4C 00 00 B5 00 95 00 4C 15 34 ;Line no. 367A 00 TABL3 ;of 00.00 00 ;ASCII 'CR' 0D ;PBEG FE ;VEND 367E FF