+Lee Stewart Posted December 6, 2011 Share Posted December 6, 2011 (edited) 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 May 21, 2012 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
Willsy Posted December 6, 2011 Share Posted December 6, 2011 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 Quote Link to comment Share on other sites More sharing options...
Willsy Posted December 6, 2011 Share Posted December 6, 2011 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 Quote Link to comment Share on other sites More sharing options...
Willsy Posted December 6, 2011 Share Posted December 6, 2011 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. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted December 6, 2011 Author Share Posted December 6, 2011 Lee, . . . Hope this helps. Mark You know it does! Thanks. ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted December 6, 2011 Author Share Posted December 6, 2011 (edited) And---here it is in TI Forth (maybe I should start another thread ): 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 December 7, 2011 by Lee Stewart 1 Quote Link to comment Share on other sites More sharing options...
marc.hull Posted December 7, 2011 Share Posted December 7, 2011 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 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted December 7, 2011 Author Share Posted December 7, 2011 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 Quote Link to comment Share on other sites More sharing options...
marc.hull Posted December 7, 2011 Share Posted December 7, 2011 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. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted December 7, 2011 Author Share Posted December 7, 2011 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 Quote Link to comment Share on other sites More sharing options...
Willsy Posted December 7, 2011 Share Posted December 7, 2011 Also, configuring pins for output that are physically wired for input (e.g. the keyboard) might not be good Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted December 7, 2011 Author Share Posted December 7, 2011 ... 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 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted December 7, 2011 Author Share Posted December 7, 2011 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 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted January 22, 2012 Share Posted January 22, 2012 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. Quote Link to comment Share on other sites More sharing options...
Willsy Posted January 22, 2012 Share Posted January 22, 2012 Yeah I think configuring the keyboard lines as output is a bad idea Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 28, 2017 Share Posted January 28, 2017 (edited) I took a different approach to the 9901 timer and always initialize it to maximum value, then read it later to compute a duration.Here is the code written in my DOS cross-compiler written in HsForth 2012 (my other hobby). The TOS is a register that holds the top of Forth stack cached. *Found a bug in this after I got my Camel Forth kernel running. TMR@ was not restarting the timer! No interrupts after 1st use. Working verison is here now. Still not happy with MS. Not reliable. Probably need to code it in ASM too. CROSS-ASSEMBLING CODE: TMR! ( -- ) \ load TMS9901 timer to max value 3FFF W 3FFF LI, \ load scratch register W with MAXIMUM timer value R12 CLR, \ CRU addr of TMS9901 = 0 0 SBO, \ SET bit 0 to 1, Enter timer mode R12 INCT, \ CRU Address of bit 1 = 2 , I'm not kidding W 0E LDCR, \ Load 14 BITs from R1 into timer R12 DECT, \ go back to address 0 0 SBZ, \ reset bit 0, Exits clock mode, starts decrementer NEXT, \ 16 bytes END-CODE CODE: TMR@ ( -- n) \ read the TMS9901 timer R12 CLR, 0 SBO, \ SET bit 0 TO 1, ie: Enter timer mode W 0F STCR, \ READ TIMER (14 bits plus mode bit) into W W 1 SRL, \ Get rid of mode bit 0 SBZ, \ SET bit 1 to zero TOS PUSH, \ make space for result TOS 3FFF LI, \ load TOS with max timer value W TOS SUB, \ subtract W from max timer value to get the duration NEXT, \ 18 bytes END-CODE \ these should be 21.3 uS ticks. MAX=3FFF : TICKS ( n -- ) TMR! BEGIN PAUSE TMR@ OVER > UNTIL DROP ; : MS ( n -- ) 2E * TICKS ; ( MAX VALUE for n = 248 mS [$F8] ) Edited January 31, 2017 by TheBF 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 29, 2017 Author Share Posted January 29, 2017 ... I see you are a new member on this forum. Welcome aboard. Only a handful of us Forthers around here. It is nice to have another on board! ...lee Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 29, 2017 Share Posted January 29, 2017 Thanks! Its great to be here. I worked in Forth shop back in the 90s and before that I was inventing projects for myself while I was a broadcast engineer at a TV station up in Canada. Still have the bug. I know Willsy here from conversations on comp.lang.forth and some email correspondence a few years back. When I get this !#@$%! cross-compiler to build a working Forth Kernel I will hit you guys up for some info the TI-99 File system. I am build a Camel Forth and want to add basic ANS File I/O words so it can extend itself. I am very impressed with the work I see you have done on TI-Forth. I cut my Forth teeth on that one But after getting a PC I left it alone for. BF Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 29, 2017 Author Share Posted January 29, 2017 Thanks for your kind words. After cooling my heels for a couple of months, I am currently back to updating the fbForth 2.0 Manual to incorporate the Addendum I published ~7 months ago as well as the new words I put in the kernel when I released the fbForth 2.0:9 cartridge binary ~4 months ago. ...lee 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 14, 2017 Share Posted February 14, 2017 I was having a heck of time with reliability reading the timer in a loop. I noticed on the Classic99 debugger that the machine would crash and end up in the GPL workspace. That seemed weird since I was not doing that so... I masked off interrupts when entering the timer read code and restored them when finished and it works now. CODE: TMR@ ( -- n) \ read the TMS9901 timer 0 LIMI, TOS PUSH, R12 CLR, 0 SBO, \ SET bit 0 TO 1, ie: Enter timer mode TOS 0F STCR, \ READ TIMER (14 bits plus mode bit) into W TOS 1 SRL, \ Get rid of mode bit 0 SBZ, \ SET bit 1 to zero 2 LIMI, NEXT, END-CODE \ these should be 21.3 uS ticks. maximum reliable value is 3FF0 due to Forth loop speed : TICKS ( n -- ) 3FFF SWAP - TMR! BEGIN DUP TMR@ > UNTIL DROP ; So now that it works but the math does not seem to be working to get real time values. For example: 333,000uS / 21.3uS = 15633 ticks (>3D11) Should give a 333mS delay right? But on Classic 99 this code run in a loop that counts to 10, runs in 12.9 seconds : 333MS ( -- ) 3D11 TICKS ; I have to use : 333MS ( -- ) 30C7 TICKS ; to make it come close to 10 secs. Could be Forth loop overhead?? BF Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 14, 2017 Author Share Posted February 14, 2017 That seems the only conclusion, but that appears excessive. I am guessing it was 30 calls to 333MS for counting 10 seconds, which would be roughly 100 ms lost per call. That is a lot of time lost! Maybe you could time the process without waiting for the timer by DROPping the > result and putting a '1' before UNTIL to see if that is indeed where the lag is. ...lee Quote Link to comment Share on other sites More sharing options...
Tursi Posted February 14, 2017 Share Posted February 14, 2017 I don't guarantee the 9901 timer is strictly accurate - I've not run any tests against it except way back in the day with Sometimes' Sudoku, and by observation determining that it seemed to be ticking off seconds roughly once per second. Do some controlled measurements to make sure the emulator is correct or just double-check your results against another one before trusting it. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 15, 2017 Share Posted February 15, 2017 Thanks for the advice. I will dig in a little more. I have 9919 driver that uses the Forth word mS coded like this. : MS ( n -- ) 015C MIN 02F * TICKS ; \ max delay= DECIMAL 348 mS And it seems correct when I did this: : BEEP ( -- ) GEN1 1398 Hz -4 dB 170 MS MUTE ;: HONK ( -- ) GEN1 218 Hz 0 dB 170 MS MUTE ; So 1 call to TICKS seems pretty accurate meaning beep and honk seem to have the right sound and I believe the System code uses 170 milliseconds for their duration. Ah well a coder's work is never done. BF Quote Link to comment Share on other sites More sharing options...
Willsy Posted February 19, 2017 Share Posted February 19, 2017 Nice to see forth stuff being done! :-) 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.