Jump to content
matthew180

Assembly on the 99/4A

Recommended Posts

1 hour ago, mizapf said:

@BeeryMiller I remember this was indeed an issue ... but long ago, maybe 10 years or more.

 

I tried it with this code. The code is meaningless, also the CLR instructions.

I made a listing file so I could follow along more easily.

 

In the LI statements, I see:

LABEL1 0007

LABEL2 0016

LABEL3 0023

 

 99/4 ASSEMBLER                                                                                   
VERSION 1.2                                                  PAGE 0001                            
  0001                   DEF START                                                                
  0002 0000   45         TEXT 'Example'                                                           
  0003            START                                                                           
  0004            LABEL1                                                                          
  0005 0008 04C0         CLR  R0                                                                  
  0006 000A 0201         LI   R1,LABEL1                                                           
       000C 0007'                                                                                 
  0007                                                                                            
  0008 000E   41         TEXT 'Another'                                                           
  0009 0016 04C0  LABEL2 CLR  R0                                                                  
  0010 0018 0201         LI   R1,LABEL2                                                           
       001A 0016'                                                                                 
  0011                                                                                            
  0012 001C   4C         TEXT 'LastOne'                                                           
  0013 0023   68  LABEL3 TEXT 'here'                                                              
  0014 0028 04C0         CLR  R0                                                                  
  0015 002A 0201         LI   R1,LABEL3                                                           
       002C 0023'                                                                                 
  0016 002E C0B1         MOV  *R1+,R2                                                             
  0017 0030 C0F1         MOV  *R1+,R3                                                             
  0018 0032 045B         RT                                                                       
  0019                   END                                                                      
  0000 ERRORS                                                                                     
~                                                                                                 
~                                                                                                 
~                                                            

This would be a good opportunity, @GDMike, to practice the LIST file option in the assembler.

 

Use options RLS  and you will get the L)ist file, plus the S)ymbol table

 

Exercises I recommend for @GDMike. I'm not giving the answers!

 

 

  • Put in DEF LABEL1 at the top, and an RT before END.
  • Put in a B @LABEL1 . Look in the list file at what the assembler puts in.

 

  • In Classic99, open the debugger window. Set a breakpoint at the address of LABEL1 (which you find in the list file)
  • Load & Run the program (you'll need that DEF LABEL1)
  • Switch to the DISASM view, and use F2 to single step. What addresses does it B)ranch to?
  • When there is an odd address in MOV *R1+,R2 what ends up in R2?

 

Granted that's emulation, but Tursi is pretty smart.

 

I put an RT at the end, and this code doesn't actually lock up. It executes some pretty stupid instructions that it makes out of the text, but it returns in the end.

 

 

  • Thanks 1

Share this post


Link to post
Share on other sites
4 hours ago, BeeryMiller said:

HEX01 BYTE >01

STR1   TEXT 'Hello World!"

HEX02 BYTE >02

HEX03 BYTE >04

STR2   TEXT 'See you another day.'

           EVEN

 

By grouping all possible odd length strings and bytes together, the use of EVEN results in at most only a single byte of extra space used if there was an odd length to all the statements.

For what it’s worth this seems to clarify matters and is a common technique I’ve observed while reading code examples. 

  • Like 3

Share this post


Link to post
Share on other sites
1 hour ago, FarmerPotato said:

Use options RLS  and you will get the L)ist file, plus the S)ymbol table

Oh really! No kiddn- I've got to do this soon. I didn't know about the symbolic table either.

I'll run through the example when time permits. But it can't be today. Boooooo

But I'll get to it.

 

 

Edited by GDMike

Share this post


Link to post
Share on other sites
*      return user to the system
       BLWP @>0000

Wondering why this will not exit my program and reset the console menu?  I forgot how to do that apparently.

Share this post


Link to post
Share on other sites
30 minutes ago, HOME AUTOMATION said:

Maybe interrupts need to be on. 

Also,

LWPI >83E0

Tried it LIMI 2 and also with LIMI 0 -- fail.

 

My code has interrupts off LIMI 0 but I inserted this LIMI 2 right before your suggested LWPI >83E0. Also tried LIMI 2 before BLWP @>0000.

 

Lots of hanging with screeching from the speakers and garbage on the screen??

 

Working on a menu to exit the game and need a way to reset the system. Kind of like an assembly END statement when the user wishes to quit. Trying to avoid a power-off-on situation.

Share this post


Link to post
Share on other sites

I think you can do a GPLlink with a

Data >0020.

In the editor assembly manual, under GPLlNK examples is what I see.

 

Edited by GDMike

Share this post


Link to post
Share on other sites
4 hours ago, Airshack said:
*      return user to the system
       BLWP @>0000

Wondering why this will not exit my program and reset the console menu?  I forgot how to do that apparently.

 

In fbForth, which has interrupts normally on, I do that after I clear the interrupt hook at >83C4:

       CLR  @>83C4
       BLWP @0000

 

There is no need to set any workspace prior to the above branch because it changes to the GPL workspace anyway.

 

...lee

  • Like 3

Share this post


Link to post
Share on other sites
3 hours ago, GDMike said:

I think you can do a GPLlink with a

Data >0020.

In the editor assembly manual, under GPLlNK examples is what I see.

 

This response is very much appreciated as I’ve been motivated to learn a few things tonight.


This is an interesting answer because I’ve avoided console routines, specifically to maintain scratchpad availability and for the speed advantages that go along with rolling my own routines. 
 

I suppose none of that matters when we’re talking about using it to exit a program. Still, seems like if a GPL-link makes this possible, shouldn’t I be able to do it more efficiently within Assembly itself?

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, Lee Stewart said:

 

In fbForth, which has interrupts normally on, I do that after I clear the interrupt hook at >83C4:

       CLR  @>83C4
       BLWP @0000


Once again, I’ll be damned if Lee Stewart didn’t get one right!

 

CLR    @>83C4
BLWP   @>0000

This did the trick even with LIMI 0.

 

Why didn’t it work without first clearing out >83C4 Lee?

  • Like 2

Share this post


Link to post
Share on other sites
4 hours ago, Airshack said:

Tried it LIMI 2 and also with LIMI 0 -- fail.

 

My code has interrupts off LIMI 0 but I inserted this LIMI 2 right before your suggested LWPI >83E0. Also tried LIMI 2 before BLWP @>0000.

 

Lots of hanging with screeching from the speakers and garbage on the screen??

 

Working on a menu to exit the game and need a way to reset the system. Kind of like an assembly END statement when the user wishes to quit. Trying to avoid a power-off-on situation.

I dunno...:dunce: from page 442 E/A manual.:grin:

  • Thanks 1

Share this post


Link to post
Share on other sites
5 hours ago, Airshack said:
*      return user to the system
       BLWP @>0000

Wondering why this will not exit my program and reset the console menu?  I forgot how to do that apparently.

I've been using that and thought it was all good. Clearing scratch-pad has been suggested earlier with similar problems. I know the state of some peripherals can interfere. Once while testing, @Ksarul had to pull a harddisk or something from the PEB, and then everything was okay.
 

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
58 minutes ago, Airshack said:


Once again, I’ll be damned if Lee Stewart didn’t get one right!

 

CLR    @>83C4
BLWP   @>0000

This did the trick even with LIMI 0.

 

Why didn’t it work without first clearing out >83C4 Lee?

Interrupts should be OFF (0) before you do the BLWP. The normal state of the TI console is interrupts disabled.

 

It didn't work because the console will jump to whatever address is stored at >83C4 before it finishes the initial initialization code. Most of the system init is done in GPL, and it is guaranteed that at least one VDP interrupt will occur before the init code finishes preparing scratchpad. (Part of the GPL interpreter will also switch interrupts on and off for each GPL instruction, but to ensure you get to the first one, they should be off).

 

Depending on the state of some of the GPLWS, you can crash the console on reboot, too. It assumes that RAM will come up without the naughty bits set. ;)

 

I actually used this to make a program that could survive reset - even a hardware reset, by making the interrupt hook jump back into my main loop. ;)

 

  • Like 3
  • Thanks 1

Share this post


Link to post
Share on other sites
12 minutes ago, Tursi said:

 

It didn't work because the console will jump to whatever address is stored at >83C4 before it finishes the initial initialization code

Where may I read more about this? I have it working as I wish yet it’s still mysterious.

  • Like 1

Share this post


Link to post
Share on other sites

Did anyone ever morph this thread into a doc, pdf, mobi format?

There was some talk on the first 2 or 3 pages of doing that thing.

 

Share this post


Link to post
Share on other sites
1 hour ago, dhe said:

Did anyone ever morph this thread into a doc, pdf, mobi format?

Not that I’m aware of at all.
 

   Just yesterday I was reviewing some program examples in the EA manual and also the Intro to Assembly Language for the TI Home Computer, by Ralph Molesworth.

 

   It appears everything written back in the day is understandably trapped in an era where mini-memory and the editor/assembler cartridges were the main tools available.

 

   It would be nice to have a guide or set of videos to watch to walk prospective TI-99/4A programmers through the modern workflows, programming techniques and lessons learned.

 

   There are some really nice resources out there for development on C-64, Atari 2600, Intellivision, etc.

 

   

  • Like 2

Share this post


Link to post
Share on other sites
7 hours ago, Airshack said:

Where may I read more about this? I have it working as I wish yet it’s still mysterious.

 

You could study TI Intern; we have a link in the Development subforum in the pinned thread.

 

I actually never thought about the effect of the 83C4 hook on [email protected] ... obviously because it is rarely used. Regarding the 83C4 alone, a LIMI 0 won't help because the next LIMI 2 occurs only 11 instructions after the BLWP @0, and it will not be cleared until then. So you must clear it before BLWP @0.

  • Like 1

Share this post


Link to post
Share on other sites

Not too much to be read, I don't think...

 

You'll find the user interrupt hook mentioned in Editor/Assembler on page 406 as "TI-99/4A only: address of the user-defined interrupt routine." That's all it has to say.

 

I don't recall where I learned how to use it, but the rest of the knowledge really comes from TI Intern

( http://harmlesslion.com/tibooks/misc/ti994a-intern.zip )

 

You'll find a brief description of the TI Interrupt Routine on page 9 (just a paragraph), and you can see the actual code that handles it on page 34:

 

0AB0 C320 MOV @>83C4,12 User defined interrupt
0AB2 83C4
0AB4 1301 JEQ >0AB8 None, then jump
0AB6 069C BL *12 Otherwise execute

 

I'm not sure if it was ever documented that on reset, at least one interrupt usually happens before the system has cleared the interrupt vector in scratchpad. I ran into it myself when working on a 1k game competition - I used the interrupt hook to directly drive my main loop so I wouldn't need any timing code, and was surprised to see that after a FCTN-= quit, my game resumed. It took a fair bit of digging to understand why.

 

Back in TI Intern, you can see the console start up on page 11. Like, all of it before GPL starts up. The reset vector starts it at >0024, and it executes just 4 CPU instructions before jumping to GPL. After 5 more instructions, the interrupt mask is toggled on/off. Depending on exactly when you reset compared to the end of frame, the first chance at an interrupt can happen this early. But after that, we're running GPL code.

 

GPL startup starts on page 99, at >0020 in this version of the GROM. That's just a direct branch to >004F. At this point GPL stops the sound interrupt, resets speech, mutes the sound chip, does some housekeeping, initializes the VDP, and then finally over 5 instructions clears most of the scratchpad RAM (it skips some parts). The interrupt hook at >83C4 is cleared in the last of these.

 

0020 : BR [email protected]>004F Power-up routine
...
004F : DCLR @>83CE Clear sound bytes
0052 : ST @>9400,>70 Load speech write
0057 : ST @>8400,>9F Set sound generators
005B : ST @>8400,>BF
005F : ST @>8400,>DF
0063 : ST @>8400,>FF
0067 : DST @>8372,>FF7E Load data/substack
006B : MOVE >0007 TO REG>01 FROM [email protected]>044E Load VDP register
0071 : CLR @>8300
0073 : MOVE >0071 TO @>8301 FROM @>8300 Clear scratch pad >00->71
0078 : MOVE >003E TO @>8382 FROM @>8300 >82->C0
007E : MOVE >000B TO @>8374 FROM @>8300 >74->7F
0083 : MOVE >0008 TO @>83C2 FROM @>8300 >C2->CA

So there are 14 GPL instructions before the hook is cleared, and before each one there is a chance for the interrupt hook to run. A cycle count with Classic99 says it takes about 35,545 cycles to reach that final MOVE (this is not including decoding and actually running it). That's roughly 11.8ms. An NTSC frame is 16.6ms, roughly, so it won't catch every time, but as you have no way of knowing when the VDP frame will hit, so that's a good 70% chance of catching it.

 

But there's one gotcha that changes that -- if you use FCTN-= to reset. If you use FCTN-= (from the interrupt routine, not hard coded), then this is checked from the interrupt routine. The interrupt routine starts on page 31 at >0900, and you'll see the first thing it does is turn interrupts back off, then check who caused it. It ends up at >94A, where it says it's clearing the interrupt with an SBO 2. As we recently discussed in another thread, that doesn't really clear anything - the VDP itself is still pending.

 

The code then meanders through checking for sprites, sound, and finally the QUIT keys on page 34 at >0A6A. You'll see that if its detected, it does the same BLWP @>0000 you were thinking about.

 

What it didn't do, though, was clear the interrupt on the VDP by reading the status register. That happens at >0A84, only if QUIT was NOT pressed. Because the VDP still asserts the interrupt, when we come back around to that very first LIMI 2 I mentioned above, the VDP will seize and and force the interrupt to run again. It will loop for as long as you hold QUIT, and then finally run one more time before letting the reset actually finish. If you've ever pressed QUIT during an XB program with moving sprites and noticed the sprites speed up - that's why. It's just spinning the VDP interrupt as fast as it can until you release the keys!

 

Of course, most interrupt handlers are well-behaved, so calling it one more time during the reset usually won't do anything. You tend to get crashes when interrupts were disabled, and general data was stored there instead of an address.
 

  • Like 4
  • Thanks 1

Share this post


Link to post
Share on other sites
On 11/14/2020 at 11:16 PM, Lee Stewart said:

 

In fbForth, which has interrupts normally on, I do that after I clear the interrupt hook at >83C4:

       CLR  @>83C4
       BLWP @0000

 

 

I should have added that the TI developers of TI Forth did not clear the interrupt hook at >83C4, but did fill low expansion RAM with >4E4F (“NO”), which guarantees (I think) there is not a single, executable, TMS9900 instruction present in low RAM to be accidentally executed. Perhaps, one reason for their filling low RAM with inexecutable “NO”s is that is where the TI Forth (and fbForth, of course) Interrupt Service Routine (ISR) is located and see @apersson850’s post #1174 for another possible reason.

 

I remember musing in one of the fbForth development threads several years ago about whether the above scattering of “NO”s was really necessary and would not the “BLWP @0” be sufficient, when @Tursi responded that I should, at the very least, clear the ISR hook at >83C4.

 

...lee

Edited by Lee Stewart
CORRECTION
  • Like 2

Share this post


Link to post
Share on other sites
2 hours ago, Tursi said:

0020 : BR [email protected]>004F Power-up routine ... 004F : DCLR @>83CE Clear sound bytes 0052 : ST @>9400,>70 Load speech

Thus the screeching from corrupted data at >9400 I guess automation recalled, unless he's got the screeching version console. Hmmm...paid extra for that

  • Like 1

Share this post


Link to post
Share on other sites
6 hours ago, GDMike said:

Thus the screeching from corrupted data at >9400 I guess automation recalled, unless he's got the screeching version console. Hmmm...paid extra for that

Yeah, the sound chip comes up in a random state, which usually means all four channels blaring. It takes an audible amount of time to make it to the mutes, so even if the console is working correctly you usually hear a little bit of it. ;)

 

  • Like 2

Share this post


Link to post
Share on other sites
10 hours ago, Tursi said:

 

 

What it didn't do, though, was clear the interrupt on the VDP by reading the status register. That happens at >0A84, only if QUIT was NOT pressed. Because the VDP still asserts the interrupt, when we come back around to that very first LIMI 2 I mentioned above, the VDP will seize and and force the interrupt to run again. It will loop for as long as you hold QUIT, and then finally run one more time before letting the reset actually finish. If you've ever pressed QUIT during an XB program with moving sprites and noticed the sprites speed up - that's why. It's just spinning the VDP interrupt as fast as it can until you release the keys!
 

I'm again amazed that there are so many things (for me) still to learn in this 35-year old console. Today it's why BLWP @0, or in TI FORTH, COLD, doesn't always work due to >83C4. And now, the reason why sprites speed up on FCTN-=!

 

 

 

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

And I'm having a blast at setting screen saver mode on/off since lee told me about an address there..I'm not near my computer and can't remember off the top of my head, but there's an address that I can "inc" and it speeds up the timer to blank out the screen, or I can just keep placing a >0002 to make the screen stay on.

I'll display my code tommorow

But simple, once you see the code

I'm thinking the magic address its >83D6

But I'll look tomorrow

Edited by GDMike
  • Like 1

Share this post


Link to post
Share on other sites
20 hours ago, Lee Stewart said:

 

I should have added that the TI developers of TI Forth did not clear the interrupt hook at >83C4, but did fill low expansion RAM with >4E4F (“NO”), which guarantees (I think) there is not a single, executable, TMS9900 instruction present in low RAM to be accidentally executed. Perhaps, one reason for their filling low RAM with inexecutable “NO”s is that is where the TI Forth (and fbForth, of course) Interrupt Service Routine (ISR) is located.

The opcode >4E4F disassembles to SZC R15,*R9+. It's executable, for sure. A different reason could be that the USCD p-system doesn't start on a reset, if a "NO" is stored at >38FA. The p-system stores "99" here when running, "GO" if stopped by a fatal error and "NO" if stopped by a Halt command.

By filling the low expansion with "NO" chances are good that the p-code card remains silent when you want to run Forth. Early p-code cards didn't have the hardware disable switch at the rear, as far as I've read. I've only seen cards with the switch, though.

Edited by apersson850
  • 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...