senior_falcon Posted February 19, 2019 Share Posted February 19, 2019 (edited) Here is a first stab at making a utility that allows the use of the full 32K memory expansion for an XB program. This was only tested on a short program, but it should work fine for its intended purpose. Divide the program into two parts. The highest line numbers will go into the 8K low memory. The lowest line numbers will go into the 32K high memory where XB normally stores programs. For my testing purposes I used the HELLO program from the compiler package. Lines 5 to 30 were saved as HELLOHM and lines 40 to 120 were saved as HELLOLM. The assembled version of the program below is XB32K.OBJ To use it: CALL INIT::CALL LOAD("DSK1.XB32K.OBJ") - This sets pointers in the scratchpad to force XB to load a program into low memory. OLD DSK1.HELLOLM - and now the program is loaded into low memory CALL LINK("X") will embed this program in a short XB loader. This is sitting in low memory. It can't be edited there, but it can be saved, so let's do it. SAVE DSK1.HELLOLM1 NEW and then OLD DSK1.HELLOHM - and now the high memory segment of the program is in the 24K high memory where XB usually keeps programs, and the low memory part if the program is still sitting in the 8K of low memory. Now to merge the line number table from the program lines in low memory into the program in high memory. CALL LINK("X") - and now the line number tables have been merged so the program can find the code stored in low memory. Now save it: SAVE DSK1.HELLOHM1 Just one more thing to do: OLD DSK1.HELLOLM1 and modify line 10 so that it will "RUN DSK1.HELLOHM1" and then save the modified program. Now if you RUN "DSK1.HELLOLM1" the program is loaded into high memory, then the program is copied into low memory where it was when you originally saved it. Then HELLOHM1 is loaded and run and the program runs normally even though half of it is in the "wrong" memory location. The limitations need to be discovered. For example, you cannot resequence the program. I am not sure how much editing can be done from XB but I think it is possible for the low line numbers. This is not very sophisticated, so be sure the high line numbers go into the low memory segment and the low line numbers go into the high memory segment. Good luck! *TO USE *CALL INIT::CALL LOAD("DSK1.XB32K.OBJ") *OLD DSK1.HELLOLM *CALL LINK("X") *SAVE DSK1.HELLOLM1 *NEW *OLD DSK1.HELLOHM *CALL LINK("X") *SAVE DSK1.HELLOHM1 AORG >3FC8 *Merge Line Number Table MRGLNT MOV @>8330,R1 Beginning of Line # table MOV @>3FFC,R2 Beginning of LNT in lowmem A R2,R1 S @>3FFE,R1 DEC R1 Can now add low mem LNT to HM LNT MOV R1,@>8330 New beginning of LNT LOOP2 MOVB *R2+,*R1+ Add line number table C R2,@>3FFE JLE LOOP2 B @>006A *Copy code from high memory into low memory *This starts at >FFD0 (255,208) LI R1,>FFE6 Program ends at >FFE7 LOOP1 MOV *R1,@>3FFE->FFE6(R1) Copies >FFE6 to >3FFE DECT R1 C R1,@>8330 Pointer to start of program in high mem JHE LOOP1 Loop until program copied into low mem B @>006A Back to XB DATA >1234,>5678 Beginning and end of line # table AORG >B000 *10 CALL INIT :: CALL LOAD(8192,255,208):: CALL LINK("X"):: RUN "DSK1.HMPROG1" XBPROG DATA >000A,>FFA8,>409D,>C804,>494E,>4954,>829D,>C804 DATA >4C4F,>4144,>B7C8,>0438,>3139,>32B3,>C803,>3235 DATA >35B3,>C803,>3230,>38B6,>829D,>C804,>4C49,>4E4B DATA >B7C7,>0158,>B682,>A9C7,>0B44,>534B,>312E,>484D DATA >5052,>4F47,>0000 CREATP MOV @>8330,@>3FFC Store beginning of line number table MOV @>8332,@>3FFE Store end of line number table MOV @>8330,R1 Pointer to beginning of LNT ANDI R1,>FFFE Round down so it is even AI R1,-67 New end of LNT MOV R1,@>8332 Store it AI R1,-3 New beginning of LNT MOV R1,@>8330 Store it MOV R1,R3 Store for later AI R3,5 starting address of program LI R2,XBPROG Start of XB program LOOP3 MOV *R2+,*R1+ Copy XB program JNE LOOP3 MOV R3,@-3(R3) Update pointer to start of program LI R0,>3FFF Save program to >3FFF MOV R0,@>8384 LI R0,MRGLNT Next CALL LINK("X") will merge LN Table MOV R0,@>2000 B @>006A AORG >8330 DATA >3FC7,>3FC7 AORG >8384 DATA >3FC7,>3FC7 AORG >2000 DATA CREATP END (Newest version attached) XB32K1.zip Edited February 28, 2019 by senior_falcon 10 Quote Link to comment Share on other sites More sharing options...
+OLD CS1 Posted February 19, 2019 Share Posted February 19, 2019 Mind blown. 2 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted February 19, 2019 Share Posted February 19, 2019 I so wanted to try this with the Stratego program, but unfortunately, the initialization program is about 600 bytes too long to fit into the lower 8K... Regardless, this is revolutionary from an XB programming standpoint. Yes, the debugging process will definitely be more involved, particularly when the utility routine has to be executed every time the program is modified, but the end result will be well worth the effort. For XB programmers, the original 24K limit is like having range anxiety with an electric car, as there is no worse feeling than to see the out of memory error pop up its ugly head just when the program is nearly done! Now we have a range extender! 4 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 19, 2019 Author Share Posted February 19, 2019 Yes, the debugging process will definitely be more involved, particularly when the utility routine has to be executed every time the program is modified, but the end result will be well worth the effort. This may not be necessary. Let's say you have 6K of subroutines from line 10000 and up. Once these have been moved into low memory and added to the line number table you may be able to merge or add lines to the main program as desired. If two programs use the same subroutines, once they have been copied into low memory, you should be able to load and run either program as desired. (Assuming the line number tables have been updated in both programs) Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 19, 2019 Author Share Posted February 19, 2019 Here's another tool that could be useful. With XB256 you have the ability to run an XB program from another XB program (nothing unusual there), but all the variables are automatically passed to the new program. In principle you could use this to run a gigantic XB program in sections. 4 Quote Link to comment Share on other sites More sharing options...
Airshack Posted February 19, 2019 Share Posted February 19, 2019 BASICally cool. Sent from my iPhone using Tapatalk Pro 2 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted February 20, 2019 Share Posted February 20, 2019 Here's another tool that could be useful. With XB256 you have the ability to run an XB program from another XB program (nothing unusual there), but all the variables are automatically passed to the new program. In principle you could use this to run a gigantic XB program in sections. With Stratego, I save the variables from the initialization program to disk before loading the main program then retrieving the variables back from disk. This introduces quite a bit of delay. What are the limitations of XB256? 2 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 20, 2019 Author Share Posted February 20, 2019 (edited) With Stratego, I save the variables from the initialization program to disk before loading the main program then retrieving the variables back from disk. This introduces quite a bit of delay. What are the limitations of XB256? I don't think this would work for you in this application. You can only use subprograms in the first program. I don't remember why that is, but I assume its because prescan sets pointers to the subprogram and then when you load the new program the pointers are wrong. Other than that there are just a couple things to remember. From the manual: CALL LINK(“RUNL1”,A$) RUNL1 is used to run an XB256 program from a running program. It is similar to CALL LINK(“RUN”) but with a major difference. After it loads the program it runs line #1, but without doing a prescan or resetting any of the variables of the calling program. By chaining programs together with RUNL1 you can create a very long XB256 program, and the neat thing is that bypassing the prescan means super fast start up time and that all the variables are automatically carried over into the newly loaded program. You don't have to save them in one program and retrieve them in the next. RUNL1 plays some tricks on the XB interpreter and to avoid problems, there are a couple of things that are important to observe. 1 – The initial program can be saved in either program format or IV254 format. 2 – A program being loaded with RUNL1 must be in IV254 format 3 – The chained programs must start with line 1 4 – A program being loaded with RUNL1 cannot be longer than the initial XB program at the beginning of the chain. A "Memory Full" error message will be issued if the chained program is longer than the original program. If it is in IV254 format you can use RUNL1 to load and return to the initial program. 5 – Because prescan is not performed, the chained programs cannot introduce new variables. They must all be contained in the first program. If the second program uses HCHAR, and X,Y,Z and A$ and they are not used in the first, the first program should include them this way: 10 GOTO 100:: X,Y,Z,A$::CALL HCHAR This causes XB to reserve space for the four variables and HCHAR. This line doesn't give a syntax error because XB never gets to the second part of the line. 6 – If the chained programs use DATA statements, they need to include RESTORE or RESTORE line-number so the program can find the DATA. 7 – DEF, SUB and SUBEND (named subprograms) can only be used in the initial program. GOSUB can be used in any of the programs. I forgot to mention that if necessary you can pad the initial program with remarks so it becomes the longest program. Edited February 20, 2019 by senior_falcon 2 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 20, 2019 Author Share Posted February 20, 2019 I have a revised version of XB32K that I will post tonight. Some of the rough edges are smoothed out and it is a bit easier to use 4 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 21, 2019 Author Share Posted February 21, 2019 This revision smooths out a few of the rough edges in the original program. USING XB32K Divide your XB program into two segments and save each segment. The one destined for the 8K low memory should have the highest line numbers. Let's call it PROGRAM1. The main program going to the 24K high memory should have the lowest line numbers. Let's call that segment PROGRAM2. CALL INIT::CALL LOAD("DSK1.XB32K.OBJ") - This loads some assembly code and sets pointers in the scratchpad which forces XB to load a program into low memory. OLD DSK1.PROGRAM1 - the first segment of the program is loaded into low memory. (You cannot use the paste XB feature of Classic99 for this) Now we need to create an XB loader program that can load PROGRAM1 into low memory and then load the second segment (PROGRAM2) into high memory. The loader program needs to know where to find the second segment. Type: DSK1.PROGRAM2 – then press F4. When you mess up and press Enter you get an error message. Don't panic, just type DSK1.PROGRAM2 again and press F4 this time. CALL LINK("X") runs an assembly routine that embeds PROGRAM1 into an XB loader. This reads the line directly above CALL LINK(“X”) and uses the first 15 characters as the file name for the second segment. (Or you can change the file name by editing line 10 of the program.) The program created is in low memory. It can't be edited or run there, but it can be saved. Let's do it. SAVE DSK1.PROGRAM1 NEW and then OLD DSK1.PROGRAM2 - and now the high memory segment of the program is in the 24K memory where XB usually keeps programs. The low memory part if the program is still sitting in the 8K of low memory. Now we must merge the line number table from the low memory program lines into the program in high memory. CALL LINK("X") - and now the line number tables have been merged and the program knows how to find the code stored in low memory. Now save it: SAVE DSK1.PROGRAM2 Here is where the magic happens. When you RUN "DSK1.PROGRAM1" the first segment of the program is loaded into high memory where XB thinks it should go. It copies itself back into low memory where it started out originally. Then PROGRAM2 is loaded and it runs normally even though much of it is in the "wrong" memory location. EDITING The code in PROGRAM1 is off limits. If you have to edit it you must go back to the original program to make any changes and then repeat the steps above. PROGRAM2 can be edited. You can add lines to it in the usual manner. If you change any line in PROGRAM2 then XB changes the line number pointers to PROGRAM1. These pointers must be reset – you can do this with CALL LINK(“X”). DO NOT list or run the program until you have reset the pointers. CALL LINK(“X”) when in doubt. The code that merges the line numbers is not particularly elegant. It compares the last line number of the XB program in high memory with the last line number of the code stored in low memory. If they match then it knows the tables have already been combined so it just refreshes the line number pointers. If they do not match then it combines the two line number tables. If you type NEW and CALL LINK(“X”) you can see just the code that is stored in low memory. XB32K1.zip 4 Quote Link to comment Share on other sites More sharing options...
+arcadeshopper Posted February 21, 2019 Share Posted February 21, 2019 1 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 21, 2019 Author Share Posted February 21, 2019 (edited) I wanted to use this technique on an adventure game, "The Ancient Chateau" in Tim Hartnell's book "Creating Adventure Games on your Computer." This was 26K long and seemed a good candidate for this. I was well on my way, but found some errors in the code and gave up on the project. Edited February 21, 2019 by senior_falcon Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted February 21, 2019 Share Posted February 21, 2019 I wanted to use this technique on an adventure game, "The Ancient Chateau" in Tim Hartnell's book "Creating Adventure Games on your Computer." This was 26K long and seemed a good candidate for this. I was well on my way, but found some errors in the code and gave up on the project. These errors cannot be fixed? Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 21, 2019 Author Share Posted February 21, 2019 (edited) These errors cannot be fixed? Yes, they can be fixed. But I am not motivated enough to do it. Actually, as I think back on it, I think I scanned the pages in the book, then used a program to convert the images to ascii. This conversion program is old and far from perfect, leading to lots of errors that must be tracked down. I was working on that when I discovered a version online that supposedly worked with the TI99. But there were errors in that program, and by then I was tired of the whole exercise. Edited February 21, 2019 by senior_falcon 1 Quote Link to comment Share on other sites More sharing options...
atrax27407 Posted February 21, 2019 Share Posted February 21, 2019 These days, exercise tires me out, too. 1 Quote Link to comment Share on other sites More sharing options...
Opry99er Posted February 22, 2019 Share Posted February 22, 2019 You're welcome to use my own game, Werewolves & Wanderers (based off the concepts from that particular book) I have fully commented TIdBiT source code and the XB output... it uses a good chunk of memory. Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 22, 2019 Author Share Posted February 22, 2019 You're welcome to use my own game, Werewolves & Wanderers (based off the concepts from that particular book) I have fully commented TIdBiT source code and the XB output... it uses a good chunk of memory. thanks, but what would the point be? It fits in 24K memory, so there is no need for added memory. Quote Link to comment Share on other sites More sharing options...
Opry99er Posted February 22, 2019 Share Posted February 22, 2019 It only fits there because I offloaded all my text and room descriptions to diskette. I pull one floor at a time into memory, then overwrite it with the next floor when I ascend or descend. It works fine that way--but if you just wanted an example of how XB32K can work, I can change the code to have all strings directly in memory and bypass the procedural loading. Quote Link to comment Share on other sites More sharing options...
Opry99er Posted February 22, 2019 Share Posted February 22, 2019 Actually, all in--I think there is something like 36K all told.... so it might not be a good candidate. Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted February 22, 2019 Share Posted February 22, 2019 I think a program designed from the ground up to use Harry's scheme would be the perfect candidate. Problem is one never truly knows how large an XB program is going to be at the start of the project... Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 22, 2019 Author Share Posted February 22, 2019 It only fits there because I offloaded all my text and room descriptions to diskette. I pull one floor at a time into memory, then overwrite it with the next floor when I ascend or descend. It works fine that way--but if you just wanted an example of how XB32K can work, I can change the code to have all strings directly in memory and bypass the procedural loading. That would be fine if you want to do it. Here's the way I see it. I make a tool for people to use. Let's pretend the tool is a hammer. I make the hammer. If you need help I will tell you how to hold the hammer. But I'm not gonna drive the nails for you! 3 Quote Link to comment Share on other sites More sharing options...
Opry99er Posted February 22, 2019 Share Posted February 22, 2019 Sure. That's a good metaphor. The way the program is now is just fine... i was mainly offering in case you just needed a test program for development trials. Thanks again for all the great things you do for the community. 2 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 25, 2019 Author Share Posted February 25, 2019 (edited) That would be fine if you want to do it. Here's the way I see it. I make a tool for people to use. Let's pretend the tool is a hammer. I make the hammer. If you need help I will tell you how to hold the hammer. But I'm not gonna drive the nails for you! I hope that didn't sound too grumpy. I had just spend an annoying evening trying to track down a quirk in the new XB 32K loader with the runtime in low memory. This worked fine with Asm99 and also with the TI assembler provided the program was short. Loading long programs assembled with the TI assembler would crash the loader. Asm99 would work fine. How come? It turns out that the TI assembler does CALL FILES(4) and this left XB without enough room for the buffer. After a few evenings all is well, but I will confess to some frustration about the bottomless time sink this project (the compiler) has become. Edited February 26, 2019 by senior_falcon 1 Quote Link to comment Share on other sites More sharing options...
RXB Posted February 25, 2019 Share Posted February 25, 2019 What about using the RAG SAMS assembler instead? It uses the SAMS instead to makes some pretty big files. I normally use the Ryte Data GPL Assembler but I restrict my sections to 8K GPL blocks. (Though I have gotten it to do 25K of GPL Object code once.) Would this be a good option for your compiler? Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted February 26, 2019 Author Share Posted February 26, 2019 (edited) The TI Assembler works fine. (And for what it's worth, the RAG assembler is noticeably slower) The problem was with the loader. The compiled code is loaded by a loader written in XB which then embeds the code in a 1 line XB loader program. You can save it as XB or as EA5. Long programs require that the runtime routines go into low memory. With the possibility of filling both low memory and high memory with the compiled code, where can the loader program go where it would be out of the way? Turns out it can be loaded into VDP ram and run from there. OK so far, but here is where the problem arose. When you run the assembler from the game developer's package it does a CALL FILES(4). When the assembly is complete and you return to XB, CALL FILES(4) is still in effect so you have 512 518 less bytes of memory and that is just enough so that the 8K buffer in VDP ram overlaps with the program strings. This makes XB unhappy. With Assm99 CALL FILES(3) was always in effect so no problems appeared. Anyhow the solution is simple enough once you know the problem: When the LOAD program runs it checks >8370 and does CALL FILES(3) if necessary. I also changed the code somewhat to be 100% sure there could be no conflict between the disk buffer and the strings. Edited February 26, 2019 by senior_falcon 2 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.