jasonleec Posted September 25, 2019 Share Posted September 25, 2019 I've been working through the book "Introduction to Assembly Language for the TI Home Computer" by Ralph Molesworth. Towards the end is a chapter on calling your assembled code from Basic and Extended Basic. In that example is this code: 60 CALL INIT 70 CALL LOAD("DSK1.BSCSUP") Whenever I run this code, I get this error: UNRECOGNIZED CHARACTER IN 70 It's the same behavior whether I run this in the Classic99 emulator, or on a real TI/PEB/Editor-Assembler disk in drive 1. For what it's worth, I get the exact same error when trying to do a CALL LOAD on the code I've assembled myself (that I typed in from the book). Does anyone know what causes this, or possibly have some example code showing how to pass parameters from Extended Basic to Assembly? Thank you. Quote Link to comment Share on other sites More sharing options...
+mizapf Posted September 25, 2019 Share Posted September 25, 2019 The Extended Basic loader cannot handle compressed object code. Assemble without the C option. 2 1 Quote Link to comment Share on other sites More sharing options...
RXB Posted September 25, 2019 Share Posted September 25, 2019 Could also be a bad conversion from your download and the file is corrupted which would also give you this error. Try a different download as the one you are using may be corrupt. 1 Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted September 25, 2019 Share Posted September 25, 2019 1 hour ago, jasonleec said: I've been working through the book "Introduction to Assembly Language for the TI Home Computer" by Ralph Molesworth. Towards the end is a chapter on calling your assembled code from Basic and Extended Basic. In that example is this code: 60 CALL INIT 70 CALL LOAD("DSK1.BSCSUP") Whenever I run this code, I get this error: UNRECOGNIZED CHARACTER IN 70 It's the same behavior whether I run this in the Classic99 emulator, or on a real TI/PEB/Editor-Assembler disk in drive 1. For what it's worth, I get the exact same error when trying to do a CALL LOAD on the code I've assembled myself (that I typed in from the book). Does anyone know what causes this, or possibly have some example code showing how to pass parameters from Extended Basic to Assembly? Thank you. . Like @mizapf said, that's a compressed object file which XB can't read. It was made with the 'C' option turned on. But I don't think there is anything in BSCSUP that you don't already have in XB. If I recall, BSCSUP defines the utilities NUMREF, NUMASG, STRREF, STRASG, and so on. When you do CALL INIT in XB, they are loaded for you into the >2000 RAM area. To use them from XB, you have to include EQU statements for them. These EQU are in the E/A manual. NUMASG etc are used to get values or assign to variables that come after the subroutine name. CALL LOAD("GIMME", 1, A$) would call the routine GIMME which you would write using NUMREF to get the "1" value and STRASG to put something in A$. I guess you're reading that in Molesworth. I think the utilities work exactly the same from TI BASIC and XB. It's just that for TI BASIC, your assembly code links to them with a REF, and you need BSCSUP to be loaded to provide the equivalent DEF. XB doesn't support REF at all, so you put in the proper EQU. So, I think you're reading the TI BASIC section in Molesworth, but it works a little differently in XB. If you're going to use XB, dive into the E/A manual chapter for accessing assembly routines from XB. Or look to see if Molesworth describes the XB differences you need to know about? I could be mistaken; I haven't used these things since 1989 but I spent many Saturday afternoons reading the E/A manual, struggling to get them right. I got stuck on the "UNRECOGNIZED CHARACTER" for many, many days. Eventually it paid off. Digression: I perfected a routine CALL LOAD("DRAWSC", A$) that turned A$ into graphics in a blink of the eye. A$ was a 81 character record loaded from a file, standing for a 9x9 grid of 2x2 tiles for an RPG! Assembly language power! 9x9 or 18x18 was ideal, and a DISPLAY,FIXED 81 file at 3*81 = 243 makes good use of the 256 byte disk sector. I put in my example to encourage you to do amazing things by adding A/L routines to your program! 3 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted September 25, 2019 Share Posted September 25, 2019 19 minutes ago, FarmerPotato said: Like @mizapf said, that's a compressed object file which XB can't read. It was made with the 'C' option turned on. But I don't think there is anything in BSCSUP that you don't already have in XB. If I recall, BSCSUP defines the utilities NUMREF, NUMASG, STRREF, STRASG, and so on. When you do CALL INIT in XB, they are loaded for you into the >2000 RAM area. To use them from XB, you have to include EQU statements for them. These EQU are in the E/A manual. Yes—BSCSUP contains the support routines exclusively for TI Basic, which are required to use ALC in your code. Of course, the E/A cartridge must be plugged in to do any of this. ...lee 1 1 Quote Link to comment Share on other sites More sharing options...
+adamantyr Posted September 25, 2019 Share Posted September 25, 2019 I found out the hard way that most of the Basic to Assembly stuff has not been well documented. I used trial and error to write the routines for Wizard's Doom; I even had to resort to using Classic99's debugger to figure out how to find values on the stack. The provided libraries are garbage; they're ridiculously huge (You use up most of the 8K to load it) and written for every generic case possible. You can find disassemblies of the code on the TI Tech pages, which is what I used to write my own specific routines. Once you get to a certain stage, using SYSTEX is also invaluable; that lets you embed the assembly code INTO an XB program directly and load things 1000% faster. (Albeit at the expense of flexibility; if your assembly code changes you have to remake the XB file from scratch.) 4 Quote Link to comment Share on other sites More sharing options...
jasonleec Posted September 25, 2019 Author Share Posted September 25, 2019 Thanks everyone for all of the feedback!! I'll look at it again this weekend to see if I can make any progress. I know when I compiled my own code, I only used the R parameter, but I'll re-do it just to be sure. I'll post an update with any progress. 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted September 25, 2019 Share Posted September 25, 2019 I remember it took me quite a while to figure out how to interface between Extended BASIC and assembly programs. I like the simplicity of doing the same thing with Pascal and assembly. 1 Quote Link to comment Share on other sites More sharing options...
RXB Posted September 26, 2019 Share Posted September 26, 2019 On 9/25/2019 at 8:37 AM, Lee Stewart said: Yes—BSCSUP contains the support routines exclusively for TI Basic, which are required to use ALC in your code. Of course, the E/A cartridge must be plugged in to do any of this. ...lee Just added info on this. TI Basic headers and XB headers are different in GPL. This is why DSR crash when looking in XB commands from TI Basic, or TI Basic commands from XB. The lower 8K is also arranged differently when CALL INIT is called from EA cart vs XB cart. 1 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted September 27, 2019 Share Posted September 27, 2019 As you are finding out, there are some differences when calling assembly programs from EA or MiniMemory and XB. 1 - When you CALL LINK in EA or MM basic you are given a workspace. In XB the workspace remains >83E0, the GPL workspace. You can run simple programs from this WS. If you can do that it is a little faster because it is on the 16 bit data bus. Some of the lower registers are available, probably from R0 to R7. (Not guaranteed) To avoid frustration set your own workspace with LWPI WKSP and add WKSP BSS 32 to save 32 bytes in your program for the workspace. 2 - Returning to BASIC is B *R11. To return to XB you should restore the GPL workspace with LWPI >83E0 and return with B at>006A. 3 - You need to add equates so the A/L code knows the address of the utilities such as KSCAN. Putting this all together we get: DEF START FAC EQU >834A \ NUMASG EQU >2008 NUMREF EQU >200C STRASG EQU >2010 STRREF EQU >2014 XMLLNK EQU >2018 KSCAN EQU >201C EXTENDED BASIC EQUATES VSBW EQU >2020 VMBW EQU >2024 VSBR EQU >2028 VMBR EQU >202C VWTR EQU >2030 ERR EQU >2034 / WKSP BSS 32 START LWPI WKSP your program here BK2XB LWPI >83E0 B @>006A END 4 - At some point you may want to use GPLLNK or DSRLNK. You'll notice they are not in the equates. I have used Millers Graphics GPLLNK for this. Just copy and paste this into your program ******************************************************************************* *GPLLNK AND DSRLINK FROM THE SMART PROGRAMMER ******************************************************************************** GPLWS EQU >83E0 GR4 EQU GPLWS+8 GR6 EQU GPLWS+12 LDGADD EQU >60 XTAB27 EQU >200E GETSTK EQU >166C GPLLNK DATA GLNKWS DATA GLINK1 RTNAD DATA XMLRTN GXMLAD DATA >176C DATA >50 GLNKWS EQU $->18 BSS >08 GLINK1 MOV *R11,@GR4 MOV *R14+,@GR6 MOV @XTAB27,R12 MOV R9,@XTAB27 LWPI GPLWS BL *R4 MOV @GXMLAD,@>8302(R4) INCT @>8373 B @LDGADD XMLRTN MOV @GETSTK,R4 BL *R4 LWPI GLNKWS MOV R12,@XTAB27 RTWP *DSRLNK PUTSTK EQU >50 TYPE EQU >836D NAMLEN EQU >8356 VWA EQU >8C02 VRD EQU >8800 GR4LB EQU >83E9 GSTAT EQU >837C DSRLNK DATA DSRWS,DLINK1 DSRWS EQU $ DR3LB EQU $+7 DLINK1 MOV R12,R12 JNE DLINK3 LWPI GPLWS MOV @PUTSTK,R4 BL *R4 LI R4,>11 MOVB R4,@>402(R13) JMP DLINK2 DATA 0 DATA 0,0,0 DLINK2 MOVB @GR4LB,@>402(R13) MOV @GETSTK,R5 MOVB *R13,@DSRAD1 INCT @DSRADD BL *R5 LWPI DSRWS LI R12,>2000 DLINK3 INC R14 MOVB *R14+,@TYPE MOV @NAMLEN,R3 AI R3,-8 BLWP @GPLLNK DSRADD BYTE >03 DSRAD1 BYTE >00 MOVB @DR3LB,@VWA MOVB R3,@VWA SZCB R12,R15 MOVB @VRD,R3 SRL R3,5 MOVB R3,*R13 JNE SETEQ COC @GSTAT,R12 JNE DSREND SETEQ SOCB R12,R15 DSREND RTWP Good luck! Later I will get into how to pass variables, unless someone beats me to it. 4 2 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted September 27, 2019 Share Posted September 27, 2019 Would it be possible to add a CALL LINK function that starts an ISR routine that makes a GOSUB to XB when a certain condition happens? Something like CALL LINK("COINC", 1, 2, 1000), which would GOSUB 1000 when there is overlap between sprites 1 and 2. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted September 27, 2019 Share Posted September 27, 2019 1 hour ago, Asmusr said: Would it be possible to add a CALL LINK function that starts an ISR routine that makes a GOSUB to XB when a certain condition happens? Something like CALL LINK("COINC", 1, 2, 1000), which would GOSUB 1000 when there is overlap between sprites 1 and 2. I am not exactly clear on what it is you want to do because you should not be calling an ISR. That is for the console ISR to call. You should, however, be able to write your own ISR to deal with whatever eventuality interests you. At the risk of telling you something you already know, once you get your ISR into RAM with CALL LOAD("DSK1.MYISR"), you can use CALL LOAD("",-31804,48,0) to put 3000h (change to entry point of your ISR) at 83C4h (-31804) so that the console ISR will call your ISR at each interrupt. Per the following code at the end of the console ISR, your ISR will start in GPL workspace (83E0h) via BL *R12. Your ISR should preserve the return address (R11) and should ensure it returns in GPL workspace via a Branch to the preserved return address: TIMIN1 LWPI WKSE TEST FOR SHIFT Q KEY (FCTN+= for the 4A) AB R14,@TIME INC. TIME IN STATUS BLOCK MOV @WKSC+R2+R2,R12 <---ISR R2 = >83C4, user ISR hook JEQ SNDEXT BL *R12 <---Branch and Link to user ISR SNDEXT CLR R8 CLR REGISTER FOR BASIC LWPI WKSC RESET WKSP to ISR workspace, WKSC RTWP RETURN TO CALLING ROUTINE Regarding your example for sprite overlap, why would you need an ISR? Why not something like this? CALL COINC(#1,#2,5,C) IF C THEN GOSUB 1000 Or, do you consider that too slow to catch the coincidence in a timely fashion and that, possibly, is the reason for your question? ...lee Quote Link to comment Share on other sites More sharing options...
Asmusr Posted September 27, 2019 Share Posted September 27, 2019 (edited) 1 hour ago, Lee Stewart said: Regarding your example for sprite overlap, why would you need an ISR? Why not something like this? The idea is that XB would continue running and only be interrupted when the condition happened. [I'm thinking of a particular use case] Edited September 27, 2019 by Asmusr Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted September 27, 2019 Share Posted September 27, 2019 51 minutes ago, Asmusr said: The idea is that XB would continue running and only be interrupted when the condition happened. [I'm thinking of a particular use case] Not being all that imaginative, I can only think of two ways to do this in your ISR: Change a pointer(?) in XB that would insert the GOSUB, perhaps in the same manner ON BREAK works. I do not know enough about XB internals to pull this off. In a main program loop, monitor two locations in ALC RAM that the ISR manages. In the ISR, change one location to indicate the event and start a counter at another location so the XB program can tell how many ticks ago the event happened. The XB code can zero the event and the counter when it processes them. Perhaps a stack of events and associated counters could be managed. ...lee Quote Link to comment Share on other sites More sharing options...
Tursi Posted September 27, 2019 Share Posted September 27, 2019 It's an interesting idea... only gotcha I can think of is that the ISR runs between GPL statements, meaning you could be in the middle of an XB statement. The problem with deferring any of the work to the XB program is that you lose the performance benefit of the patch - might as well just check the coincidence yourself as check a flag. This idea comes to mind, although it's not foolproof. Assuming the program has at least one GOSUB in the main loop, you might be able to manipulate the return stack. Basically, when the ISR detects collision, insert the collision handler as the next return address. On RETURN, the collision handler will be invoked. The collision handler should also exit with RETURN, which should take it back to the original return point. This gives most of the autonomy, doesn't break the interpreter by jumping in the middle of an instruction, and the delay is largely dependent on the length of the subroutine being executed at the time of collision. However.. it does have a problem when the stack is empty. I do like the ON BREAK or ON ERROR idea though.. maybe peek at how those work. If they are triggered by a flag, that may well be the hook you need, but I don't have the insight. 1 Quote Link to comment Share on other sites More sharing options...
RXB Posted September 28, 2019 Share Posted September 28, 2019 Want me to post the GPL code for ON ERROR and ON BREAK? It is in RXB GPL source. 1 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted September 30, 2019 Share Posted September 30, 2019 Here's another example of something that's (comparatively) easy to do in Pascal, as the TI implementation allows for concurrency. Thus you declare the coincidence handler as a process, which hangs on a semaphore. When that semaphore is cleared, the handler will run. The trick you have to do is write something which can clear the sempaphore on the interrupt, but then going to the right place in the high level language is pretty simple. 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.