Jump to content
IGNORED

Scrolling using TurboForth


Recommended Posts

Hi again

 

The scroll routine is now wrapped up in a single ASM word. Each call of the word scrolls 1 pixel for the entire visible string and feeds a new character every 8 scrolls. I have hooked it up to the interrupt address at $a008. This too works. However, the scrolling is not completely smooth as I kind of expected. While I have problems getting it to run at 60 frames per second with a full 33 character displayed - it runs at about 53 frames per second - which is not enough to include in an interrupt driven process, I have tested with far fewer characters displayed. But despite running at way more than the required 60 framed, it is still not smooth... if fast. Interestingly it runs smoother if placed in a loop in TF with about 29 characters displayed.

I suspect the problem has to do with synchronizing with the screen update. Does this make sense?

 

PS. How do I stop hooking up to the interrupt at $a008? Do I write a RT instruction to the address to replace the vector? The ASM code uses almost all the cpu time. So the machine is rather unresponsive, when it runs. Considering that it has to recalculate around 250 bytes, 60 times a second, I do not blame it. But it means I have to put a condition inside the scroll code to stop.

Another thing that confuses me a bit is... I read that I should end my code with an RT or R11 ** b, command. However, it only works for me without such a command. Is my information obsolete, or should it be R12?

 

regards

Jesper

 

 

Link to comment
Share on other sites

1 hour ago, jschultzpedersen said:

I suspect the problem has to do with synchronizing with the screen update. Does this make sense?

 

PS. How do I stop hooking up to the interrupt at $a008? Do I write a RT instruction to the address to replace the vector? The ASM code uses almost all the cpu time. So the machine is rather unresponsive, when it runs. Considering that it has to recalculate around 250 bytes, 60 times a second, I do not blame it. But it means I have to put a condition inside the scroll code to stop.

Another thing that confuses me a bit is... I read that I should end my code with an RT or R11 ** b, command. However, it only works for me without such a command. Is my information obsolete, or should it be R12?

 

 

 

Yes if you are moving  fast you would need to synchronize your code to the frame rate of the video. 

 

Lee posted this code few years back in another thread.

 

Quote:

Here is the ALC [corrected] to poll for the next VDP interrupt in TurboForth (WI , say), which must have the TMS9900 Assembler (block 9) loaded:

\ Wait for VDP Interrupt
ASM: WI  ( -- )
   R1 $8000 LI,            \ VDP interrupt mask
   $8802 @@ R2 MOVB,       \ reset VDP status byte
   BEGIN,                  
      $8802 @@ R2 MOVB,     \ get VDP status byte
      R1 R2 COC,           \ VDP interrupt?
   EQ UNTIL,               \ loop if not
;ASM

and, without the TMS9900 Assembler [corrected]:

\ Wait for VDP Interrupt
HEX
CODE: WI  ( -- )
   0201 8000 D0A0 8802 D0A0 8802 2081 16FC 
;CODE

...lee

  • Like 1
Link to comment
Share on other sites

Posted (edited)
1 hour ago, jschultzpedersen said:

PS. How do I stop hooking up to the interrupt at $a008? Do I write a RT instruction to the address to replace the vector? The ASM code uses almost all the cpu time. So the machine is rather unresponsive, when it runs. Considering that it has to recalculate around 250 bytes, 60 times a second, I do not blame it. But it means I have to put a condition inside the scroll code to stop.

Another thing that confuses me a bit is... I read that I should end my code with an RT or R11 ** b, command. However, it only works for me without such a command. Is my information obsolete, or should it be R12?

 

The user interrupt hook at $A008 should contain the entry address of an Assembly Language Code (ALC) routine that, indeed, ends with RT or B *11 because the ISR dispatcher calls the user ISR via a BL instruction and expects a return to its following instruction. You can write a Forth ALC routine as

ASM:  MYISR
   R0 R0 MOV,          \ dummy entry statement
   \ <do other stuff>
   RT,                 \ return to caller
;ASM

 

 and put the pfa (Parameter Field Address or body address) in $A008:

' MYISR       \ tick MYISR to get its cfa (Code Field Address)
>BODY         \ get its pfa, where ALC of MYISR starts
$A008 !       \ stash entry address into user ISR hook

 

To stop processing the user ISR, merely store a zero there. You could also store the pfa of your ISR code in a constant to later restart the user ISR.

 

...lee

Edited by Lee Stewart
clarified
  • Like 3
Link to comment
Share on other sites

I was looking at code from the Legend of Tilda and saw something I had seen before.

I found this in my library folder. 

 

It reads the CRU bit on the 9901 chip to test for vertical interval synchronization. 

Don't know it is any better or not.  My version PUSHs and POPs R12 onto the return stack so it is safe to use in the middle of other I/O operations. 

 

Also I added an extra line from the legend of Tilda version to clear the CRU bit before testing bit 2.

 

That could be removed but if you want to keep it here are the definitions for RPUSH, and RPOP,  which should work in TForth as well. ( might need to change *RP  and *RP+ ?? ) 

 

: RPUSH, ( src -- ) 
    	RP DECT, 
    ( src) *RP  MOV, ;  \ compiles 4 bytes
    
: RPOP,  ( dst -- ) 
    *RP+  SWAP MOV, ;  \ compiles 2 bytes

 

 

\ vsync.fth  adapted from code by @PeteE on Atariage.com   12JUL2022

NEEDS MOV, FROM DSK1.LOWTOOLS

HEX
8802 CONSTANT VDPSTA

CODE VSYNC  ( -- )
      R12 RPUSH,
      VDPSTA @@ R12 MOVB,    \ Clear interrupt flag manually
      R12 CLR,               \ 9901 CRU base Address= 0
      BEGIN,
        2 TB,                \ Test CRU bit 2 - VDP INT
      EQ UNTIL,              \ Loop until set
      VDPSTA @@ R12 MOVB,    \ Clear interrupt flag manually since we polled CRU
      R12 RPOP,
      NEXT,
      ENDCODE

      DETACH                 \ removes the assembler from Low RAM

 

  • Like 4
Link to comment
Share on other sites

Hi

 

I used the code supplied by TheBF to wait for VDP Interrupt as a word in TF ( called WI) before calling my scroll routine as well as as an integral part of the assembler code. This works fine insofar as to give a relatively stable display of the scrolling text 30 times a second. The bad news is, that the scroll routine is too slow to run 60 times a second (It does about 53 / s). So with the synch word as a delay it runs every second VDP interrupt instead. During a walk I had an idea for an alternative algorithm, that should be faster. So the next few day will show if this works out to more than 60 / s. Anyway, the information provided has given me a good idea of how to work with assorted interrupts, Thanks everybody!

 

regards

Jesper

  • Like 2
Link to comment
Share on other sites

Hi again

My scrolling project seems to have hit it highwater mark. I can execute 800 scrolls in about 11 seconds with 27 characters. The ASM code is divided into five words. These words are executed from within a loop in TurboForth. This is faster than 60 times a second, but obviously the computer has other things to attend to besides running my code. So turning the words into one ASM word and executing it via interrupt is not possible for me. The system locks up after the first scroll. The good news is, that it runs fine when synchronized with the WI (wait for VDP interrupt) routine published earlier. 800 loops run in about 13 seconds with a fairly stable movement and only an occasional stutter. This matches 60 updates per second. So I guess I made it! The tricks and tips provided by your comments have helped a lot reaching the final result, which spans 6 blocks. If anyone is interested in the code, I can publish it here. I will probably write a commented version of the code for an article in the TI*MES magazine. But it may take a year before it gets published, since there are 2 issues per year, and I have another article queued up already.

 

My inabillty to run it using interrupt raises a question. How many instructions can you execute in an interrupt routine? I have no problem running words with, say, 15-20 instructions via interrupt. But thousands..... no way near it.

 

regards

Jesper

Link to comment
Share on other sites

Congratulations Jesper. Sounds like you had some fun with this one and gathered some insights into not just Forth but this old hardware as well. 

 

How many instructions in an interrupt. Good question. 

 

Grabbing a napkin and turning it over ... ;) 

 

We would probably never want the interrupt to consume more that 10% of the CPU so let's just assume that as a requirement. 

That means in a North American machine we have 1/60 of a second, or 16.666mS / 10 = 1.666 mS to work with or 1666uS

This is what kills the 9900. Cycles per instruction is ghastly. :)

 

I just took a random freeze of instructions running in ROM with the E/A Cartridge plugged in. 

OMG look at the 2nd last instruction.

   0496  045B  b    *R11                   (12)
   0070  0300  limi >0002                  (16)
   0074  0300  limi >0000                  (16)
   0078  D25D  movb *R13,R9                (41)
   007A  1105  jlt  >0086                  (8)
   007C  D109  movb R9,R4                  (14)
   007E  09C4  srl  R4,12                  (36)
   0080  C164  mov  @>0c36(R4),R5          (22)
   0084  0455  b    *R5                    (12)
   011A  D120  movb @>837c,R4              (22)
   011E  0A24  sla  R4,2                   (16)
   0120  11FA  jlt  >0116                  (8)
   0122  D81D  movb *R13,@>83f3            (49)
   0126  0249  andi R9,>1fff               (14)

 

The average clock time for these instructions is  20.4 per instructions. 

 

Total clocks are 284 so this would run in 284 x .333uS = 95uS 

But this is on the 16 bit buss, so full processor speed. 

 

If our code is running in the Expansion Ram we need to add about ~25% , so this would take 119uS running on the 8 bit buss. 

And the average would go up to 25uS per instruction. 

 

So 1666 uS /  25 uS ~= 67 instructions.  

 

On a European machine we would have a bit more at 50Hz video rate. 

 2000uS / 25uS  = 80 instructions for 10% of the CPU time. 

 

At least that's what my napkin says. 

 

  • Like 1
Link to comment
Share on other sites

Hi

Ok. Less that one hundred instructions per interrupt cycle. No wonder it did not work even if I only scrolled a few characters (In fact that almost works with just two characters). I can see that if I try around 30 characters, it start running at 30 fps instead of 60 fps when using the WI routine to synchronize. That means it takes more than 1/60 of a second to process the scroll. I put a lot of effort into managing everything using registers for optimum speed. I had to use variables for a few counters, as I was running out of registers in the most complex code section (the actual scroll calculations) It paid off, since the execution speed grew from about 50 with my first attempts to eventually about 70 executions per second. It also helped, when I realised that I could process the patterns in 16 bits instead of 8 bits at a time. This saves a lot on SLA instructions though the handling of 'overflowing' bits between patterns gets a little more complicated.

 

I have included a .txt file with the raw code if someone is interested. Notice the last block (number 50). This is where I store the text for scrolling. This is reflected in the stack parameters to the TSETUP word. (Block 50, 100 characters).

 

regards

Jesper

Scroll code tf.txt

  • Like 2
Link to comment
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.
Note: Your post will require moderator approval before it will be visible.

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