Jump to content

Photo

TurboForth Assembler

Forth TI Forth TurboForth Forth Assembler

14 replies to this topic

#1 Lee Stewart OFFLINE  

Lee Stewart

    Stargunner

  • 1,384 posts
  • Location:Maryland

Posted Mon Dec 5, 2011 11:33 PM

I decided to move my explorations into TurboForth Assembler to a different thread (this one) rather than hijacking another thread. My post here (http://www.atariage....ost__p__2415039) alludes to my current project, viz., implementing Tursi's TMS9900-Assembler-coded 14-bit countdown timer (http://www.atariage....ost__p__2021341) using the TMS9901 Programmable Systems Interface and based on Thierry Nouspikel's code ("TMS9901" link at http://www.nouspikel...titechpages.htm).

My two Forth words have the same name as Tursi's TMS9900 Assembler code: INIT01 to start the timer and READ01 to get its current value. My TurboForth Assembler code follows:

ASM: INIT01	 ( --- )
   R12 R2 MOV,		   \ Save return address
   R12 CLR,		   \ CRU base of the TMS9901
   0 SBO,			   \ Enter timer mode
   R1 $3FFF LI,		   \ Maximum value
   R12 INCT,		   \ Address of bit 1
   R1 14 LDCR,		   \ Load value
   R12 DECT,
   0 SBZ,			   \ Exit clock mode, start decrementer
   R2 R12 MOV,		   \ Restore return address
;ASM

ASM: READ01	( --- n )
   R12 R2 MOV,		   \ Save return address
   R12 CLR,		   \ CRU base of the TMS9901
   0 SBO,			   \ Enter timer mode
   SP INCT,		   \ Make space on stack to leave timer value
   *SP 15 STCR,		   \ Read current value (plus mode bit) and put on stack
   *SP 1 SRL,		   \ Get rid of mode bit
   0 SBZ,			   \ Exit clock mode, decrementer continues
   R2 R12 MOV,		   \ Restore return address
;ASM

Both words seem to compile OK. INIT01 appears to work, but I cannot tell for sure because executing READ01 causes a stack underflow, which baffles me. Any ideas as to what is wrong?

...lee

Edited by Lee Stewart, Mon May 21, 2012 9:06 AM.


#2 Willsy ONLINE  

Willsy

    Stargunner

  • 1,872 posts
  • Location:Uzbekistan (no, really!)

Posted Tue Dec 6, 2011 2:11 AM

Hmmm... that's weird. Looks fine to me. Did you single step it to make sure (using the classic 99 debugger)?

I'm wondering if the indirect store is legal on a store cru instruction?

In other words, rather than

STCR *Rx,y

Maybe it needs to be done in two steps...

STCR Rx,y
MOV Rx,*SP

I think that may be the source of your trouble. I'm guessing you can only store CRU bits directly to a register, thus your assembly instruction, as currently coded is producing an op-code that actually means something else.

Plausible?

Mark

#3 Willsy ONLINE  

Willsy

    Stargunner

  • 1,872 posts
  • Location:Uzbekistan (no, really!)

Posted Tue Dec 6, 2011 5:37 AM

Lee,

I just got a chance to try your code. I was right and wrong at the same time! Right theory, wrong instruction! I should have taken the time to study your code properly, but in my defence I had just got out of bed and was extremely bleery eyed, and wasn't even wearing my glasses!

The problem is the SRL instruction:

*SP 1 SRL,			  \ Get rid of mode bit

You can't indirectly shift a value. You can only shift a value in a register, not a value pointed to by a register (a limitation of the 9900 instruction set). When I check the code with the debugger, the instruction is simply interpreted as SRL R4,1 - this has the effect of dividing the stack pointer (not the value on the top of the stack!) by two. The interpreter in TF runs DEPTH at the end of each line of input to deduce if the stack underflowed or not, and correctly deduced a (rather huge) stack underflow!

You need to get the value directly in a register, shift it, and write it to the stack.

So, you'd need to re-write it slightly, maybe like this:

ASM: READ01	 ( --- n )
   R12 R2 MOV,	   \ Save return address
   R12 CLR,		  \ CRU base of the TMS9901
   0 SBO,		    \ Enter timer mode
   R1 15 STCR,	   \ Read current value (plus mode bit) into r1
   R1 1 SRL,		 \ Get rid of mode bit
   SP INCT,		  \ make space in stack
   R1 *SP MOV,	   \ place value on stack
   0 SBZ,		    \ Exit clock mode, decrementer continues
   R2 R12 MOV,	   \ Restore return address
;ASM

This assembles and executes as expected. I used the following test harness:

: TEST INIT01 READ01 . ;

However, it always returns 16381 (using Classic99). This may simply be because of the interval between INIT01 and READ01 being the same each time TEST is executed? Not sure. I know zilch about the 9901, I'd have to study the data sheet for the chip before I could comment further. But anyway, that's the stack fault sorted ;)

Hope this helps.

Mark

#4 Willsy ONLINE  

Willsy

    Stargunner

  • 1,872 posts
  • Location:Uzbekistan (no, really!)

Posted Tue Dec 6, 2011 7:01 AM

Yep. You're setting the countdown timer to >3FFF in INIT01 and my little TEST word is immediately calling READ01 and returning with 16381 (>3FFD), so, 2 'ticks' occurred "between" INIT01 and READ01. That's 666nS. This would be the time taken by NEXT (the inner interpreter right at the heart of TF) and the first three instructions of READ01 if I'm not mistaken (because that's when you get the sample from the 9901).

One unit on the decrementer represents 64 clock periods. Well, we had two clock units, so that's 128*333ns=42.624uS.

I think. :)

Interesting.

#5 Lee Stewart OFFLINE  

Lee Stewart

    Stargunner

  • Topic Starter
  • 1,384 posts
  • Location:Maryland

Posted Tue Dec 6, 2011 7:28 AM

Lee,
.
.
.
Hope this helps.

Mark


You know it does! :P Thanks.

...lee

#6 Lee Stewart OFFLINE  

Lee Stewart

    Stargunner

  • Topic Starter
  • 1,384 posts
  • Location:Maryland

Posted Tue Dec 6, 2011 4:44 PM

And---here it is in TI Forth (maybe I should start another thread :twisted: ):

HEX		   ( Put Forth into hexadecimal mode)
CODE INIT01	 ( --- )
   C CLR,		   ( CRU base of the TMS9901)
   0 SBO,		   ( Enter timer mode)
   1 3FFF LI,		   ( Maximum value)
   C INCT,		   ( Address of bit 1)
   1 E LDCR,		   ( Load value)
   C DECT,
   0 SBZ,		   ( Exit clock mode, start decrementer)
NEXT,

CODE READ01	 ( --- n )
   C CLR,		   ( CRU base of the TMS9901)
   0 SBO,		   ( Enter timer mode)
   1 F STCR,		   ( Read current value [plus mode bit])
   1 1 SRL,		   ( Get rid of mode bit)
   SP DECT,		   ( Make space on stack to leave timer value)
   1 *SP MOV,		   ( Timer value to stack)
   0 SBZ,		   ( Exit clock mode, decrementer continues)
NEXT,
DECIMAL		   ( Back to decimal mode)

The differences are as follows:
  • No need to save/restore R12 because TI Forth expects it to be used for the CRU.
  • Registers that do not have special names (SP, RP, etc.) are referenced by number only (I may change this in my copy of TI Forth! I have no idea why the TI programmers didn't do it because TMS9900 Assembler certainly has it that way.)
  • Used HEX before definitions because there is no explicit entry of hexadecimal numbers in TI Forth as there is in TurboForth (with a '$' prefix). This required all numbers, including register numbers, to be expressed in hexadecimal notation.
  • SP (stack pointer) is decremented (SP DECT,) in TI Forth to make space because the stack grows downward from high memory towards HERE .
  • CODE and NEXT, in TI Forth are pretty much equivalent to ASM: and ;ASM of TurboForth. Personally, I like the balance of the TurboForth words much better!
This code works quite the same as the TurboForth code above with Willsy's modifications in READ01 . Each tick of the timer is 23.33 µs 21.33 µs. The cycle time of the timer is 349.5 ms to complete 16384 ticks (16383 - 0, 3FFFh - 0). As Tursi indicated in my reference somewhere above, the timer can be started with a different maximum to cause it to cycle faster. For example, using 3FFh as the maximum value would start a timer with 1024 ticks and a cycle time of about 24 ms. Similarly, FFh as the maximum value would provide 256 ticks and a cycle time of about 6 ms. Perhaps I will redefine INIT01 to take a value from the stack for the timer's maximum value. The word should probably mask off the leftmost two bits to insure the value is no more than 14 bits wide, though, perhaps they are ignored. I will check.

...lee

Edited by Lee Stewart, Wed Dec 7, 2011 12:40 PM.


#7 marc.hull OFFLINE  

marc.hull

    Dragonstomper

  • 935 posts
  • Location:Oklahoma CIty.

Posted Tue Dec 6, 2011 6:06 PM

A word of caution derived from a bad experience. While using the 9901 timer as a period clock I burnt out 2 9901's by entering and leaving the clock mode too fast. I was checking and rechecking instantly for a value to occur so I don't think your code will cause issues but maybe a note of caution here......

I

#8 Lee Stewart OFFLINE  

Lee Stewart

    Stargunner

  • Topic Starter
  • 1,384 posts
  • Location:Maryland

Posted Tue Dec 6, 2011 7:15 PM

A word of caution derived from a bad experience. While using the 9901 timer as a period clock I burnt out 2 9901's by entering and leaving the clock mode too fast. I was checking and rechecking instantly for a value to occur so I don't think your code will cause issues but maybe a note of caution here......

I


Marc...

Wow! Good to know. Is the 9901 located in a not-very-well-ventilated spot in the console or just vulnerable to heavy activity? Do you have any idea whether the maximum cycle time is energetically easier on the chip than shorter cycle times? Or, do you think it wise to employ a STOP01 word or to otherwise use it only intermittently?

...lee

#9 marc.hull OFFLINE  

marc.hull

    Dragonstomper

  • 935 posts
  • Location:Oklahoma CIty.

Posted Tue Dec 6, 2011 9:13 PM


A word of caution derived from a bad experience. While using the 9901 timer as a period clock I burnt out 2 9901's by entering and leaving the clock mode too fast. I was checking and rechecking instantly for a value to occur so I don't think your code will cause issues but maybe a note of caution here......

I


Marc...

Wow! Good to know. Is the 9901 located in a not-very-well-ventilated spot in the console or just vulnerable to heavy activity? Do you have any idea whether the maximum cycle time is energetically easier on the chip than shorter cycle times? Or, do you think it wise to employ a STOP01 word or to otherwise use it only intermittently?

...lee


I don't think it's a heat issue and I also don't know what a stop01 word is. I have a feeling it is an issue of mode changing and reading bits to fast. It would be nice to know exactly how long the 9901 takes to change modes and the exact effect of writing data to output pins.

I don't have the technical data Lee only the bad experience. I think it stemmed from reading the clock, writing it back and starting the clock over and over in a tight loop.

#10 Lee Stewart OFFLINE  

Lee Stewart

    Stargunner

  • Topic Starter
  • 1,384 posts
  • Location:Maryland

Posted Tue Dec 6, 2011 9:44 PM

I don't think it's a heat issue and I also don't know what a stop01 word is. I have a feeling it is an issue of mode changing and reading bits to fast. It would be nice to know exactly how long the 9901 takes to change modes and the exact effect of writing data to output pins.

I don't have the technical data Lee only the bad experience. I think it stemmed from reading the clock, writing it back and starting the clock over and over in a tight loop.


Sorry. A "word" in this context is a Forth function/routine and I was just suggesting a name (STOP01) without explanation (my bad) for a function I might write that would stop the timer. But, if your intuition is right, that would probably not be necessary. Thanks for your input.

...lee

#11 Willsy ONLINE  

Willsy

    Stargunner

  • 1,872 posts
  • Location:Uzbekistan (no, really!)

Posted Wed Dec 7, 2011 4:48 AM

Also, configuring pins for output that are physically wired for input (e.g. the keyboard) might not be good :-o

#12 Lee Stewart OFFLINE  

Lee Stewart

    Stargunner

  • Topic Starter
  • 1,384 posts
  • Location:Maryland

Posted Wed Dec 7, 2011 12:57 PM

...

Perhaps I will redefine INIT01 to take a value from the stack for the timer's maximum value. The word should probably mask off the leftmost two bits to insure the value is no more than 14 bits wide, though, perhaps they are ignored. I will check.

...lee


There would be no need to mask the input of the maximum value for the counter because the TI Forth line, 1 E LDCR, , ( 1 14 LDCR, in decimal mode and R1 14 LDCR, in TurboForth) will only load the low-order 14 bits, effectively masking off the high-order 2 bits.

...lee

#13 Lee Stewart OFFLINE  

Lee Stewart

    Stargunner

  • Topic Starter
  • 1,384 posts
  • Location:Maryland

Posted Wed Dec 7, 2011 1:53 PM

OK...Here's a version of INIT01 that takes the maximum value for the counter from the stack:

TurboForth---
ASM: INIT01	 ( n --- )
   R12 R2 MOV,		   \ Save return address
   R12 CLR,		   \ CRU base of the TMS9901
   0 SBO,		   \ Enter timer mode
   *SP R1 MOV,		   \ Maximum value from stack to R1
   SP DECT,		   \ Decrement stack pointer
   R12 INCT,		   \ Address of bit 1
   R1 14 LDCR,		   \ Load value
   R12 DECT,
   0 SBZ,		   \ Exit clock mode, start decrementer
   R2 R12 MOV,		   \ Restore return address
;ASM

TI Forth---
HEX		   ( Put Forth into hexadecimal mode)
CODE INIT01	 ( n --- )
   C CLR,		   ( CRU base of the TMS9901)
   0 SBO,		   ( Enter timer mode)
   *SP+ R1 MOV,		   ( Max val: stack to R1; reduce stack)
   C INCT,		   ( Address of bit 1)
   1 E LDCR,		   ( Load value)
   C DECT,
   0 SBZ,		   ( Exit clock mode, start decrementer)
NEXT,
DECIMAL		   ( Back to decimal mode)

Notice in the TI Forth code that *SP+ R1 MOV, both copies the stack value and auto-increments the stack pointer (reducing stack size in TI Forth), thus obviating the necessity for a separate SP INCT, line ;-).

...lee

#14 apersson850 OFFLINE  

apersson850

    Chopper Commander

  • 103 posts

Posted Sun Jan 22, 2012 12:02 PM

I don't remember the details, but it's possible to program the TMS 9901 in such a way that you destroy it in the 99/4A. That has to do with the fact that the chip has pins that can be either inputs or outputs on the same pin, and as written above, connecting outputs to each other isn't a good idea.
I've had to replace my 9901 in my 99/4A too, although not due to a programming mistake. Mine is now socketed.

#15 Willsy ONLINE  

Willsy

    Stargunner

  • 1,872 posts
  • Location:Uzbekistan (no, really!)

Posted Sun Jan 22, 2012 3:03 PM

Yeah I think configuring the keyboard lines as output is a bad idea :)





Also tagged with one or more of these keywords: Forth, TI Forth, TurboForth, Forth Assembler

0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users