Jump to content
jasonleec

Assembler from Extended Basic issue

Recommended Posts

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.

Share this post


Link to post
Share on other sites

The Extended Basic loader cannot handle compressed object code. Assemble without the C option.

  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites

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.

  • Like 1

Share this post


Link to post
Share on other sites
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!

 

  • Like 3

Share this post


Link to post
Share on other sites
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

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

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.)

  • Like 4

Share this post


Link to post
Share on other sites

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.

  • Like 2

Share this post


Link to post
Share on other sites

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.

  • Like 1

Share this post


Link to post
Share on other sites
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.

  • Like 1

Share this post


Link to post
Share on other sites

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.

  • Like 4
  • Thanks 2

Share this post


Link to post
Share on other sites

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.

 

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 by Asmusr

Share this post


Link to post
Share on other sites
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:

  1. 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.
  2. 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

Share this post


Link to post
Share on other sites

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.

 

  • Like 1

Share this post


Link to post
Share on other sites

Want me to post the GPL code for ON ERROR and ON BREAK?

 

It is in RXB GPL source.

  • Like 1

Share this post


Link to post
Share on other sites

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.

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...