Jump to content


+AtariAge Subscriber
  • Content Count

  • Joined

  • Last visited

Everything posted by TheBF

  1. This is fun. I have been playing with sound lists and the 9919. Maybe I can come up with a sound or 2 for you.
  2. I hope I am not breaking any rules by posting a reference to another discussion site but for those interested in reading/learning about Forth I just found out about there are healthy discussions going on at: http://forum.6502.org/viewforum.php?f=9 I suspect our TI-99 group is smaller than the 6502 crowd so it probably does not make sense to partition up into narrow forums here. And of course since we are all upper crust 16 bit types, we are interested in a broader range of topics, unlike those 8 bitters down the street.
  3. Finally go around to making INLINE[ ] work without the need to create a new CODE word. This version relies on 2 new words. ASM[ ]ASM. By themselves these words let you insert inline assembly language inside a Forth definition, like you might see in a C compiler. ASM[ simply compiles the addresses into the Forth list of addresses that make it look like a new CODE word. (Notice why it's called "indirect threaded code". (ITC) It take 2 addresses to point to a machine language word.) ASM[ also turns off the compiler! Fun FACT: The Forth assembler interprets text. The only compiler is the comma operator which "compiles" a number into memory. ]ASM is a macro that compiles 2 instructions after the inline machine code LI R9,HERE+4 B *R10 R9 is the Forth instruction pointer (IP). We need to advance IP past the machine code that we compiled into the definition R10 holds the address of the NEXT routine, which is the 3 instruction Forth "inner" interpreter in ITC Forth. An example of what can be done with it is shown in this COMPARE routine that is written in Forth. There are long strings of CODE WORDs in this routine that are wasting time between each word by going through the inner interpreter. When we inline[ ] the words inside the loop we get a 56% speed up. There is no reason to inline the words outside the loop because the size will increase with little improvement. HEX : A$ S" THIS IS A LONG STRING FOR COMPARING TO ANOTHER" ; : B$ S" THIS IS A LONG STRING FOR COMPARING TO ANOTHER TOO" ; : COMPARE ( addr1 u1 addr2 u2 --- diff ) \ Compare two strings. diff is negative if addr1 u1 is smaller, 0 if it \ is equal and positive if it is greater than addr2 u2. ROT 2DUP - >R \ compute U2-U1, push to RSTACK. MIN DUP IF >R \ loop counter push to RSTACK BEGIN OVER [email protected] OVER [email protected] - IF SWAP [email protected] SWAP [email protected] - 2R> 2DROP \ clean Rstack EXIT \ & get out of this word THEN CHAR+ SWAP CHAR+ SWAP \ incr. addr2 & addr1 R> 1- DUP >R \ get rstack loop counter, decr, copy, push back 0= UNTIL \ loop until loop counter=0 R> \ get the U2-U1 value THEN DROP 2DROP R> NEGATE ; : COMPTEST 100 0 DO A$ B$ COMPARE DROP LOOP ; \ 9.4 seconds : FCOMPARE ( addr1 u1 addr2 u2 --- diff ) ROT 2DUP - >R MIN DUP IF >R BEGIN INLINE[ OVER [email protected] OVER [email protected] - ] IF INLINE[ SWAP [email protected] SWAP [email protected] - 2R> 2DROP ] EXIT THEN INLINE[ CHAR+ SWAP CHAR+ SWAP R> 1- DUP >R ] 0= UNTIL R> THEN DROP 2DROP R> NEGATE ; : FCOMPTEST 100 0 DO A$ B$ FCOMPARE DROP LOOP ; \ 6 seconds In the new version I also removed some the word names and just put the code inside the definition of INLINE[. This bad form in Forth which favours short definitions, but I wanted to save some space. TI-99 is not the most generous environment. New version is in the spoiler for those who are curious.
  4. That was quick. So that is about 18..20 bytes ? So you save 24 or so. If it works like you need it to, that sounds like a winner.
  5. I didn't look at how you use it exactly, but I created a break word that calls the ROM routine once looking for key pressed and then waits in a loop until the key is released. That works very reliably. If you don't like the action to happen on key release, then the key release loop could include a countdown timer. Since calling the routine is just one BL , you might still save space by including the fancy key release timer loop. My 2 cents Canadian (only worth 1.6 cents USD ) B
  6. Ok thanks for the pointers. I will take another run at it. I want to make use of Forth functions for the mundane stuff like file name massaging and such. I have some cool little code routines called SKIP and SCAN that let me fly through strings. So I want to set things up with Forth to reuse code as much as possible and then kick off the OS to do its magic and come back to me. We shall see if I can figure it out. B
  7. I will send you my consultation invoice. Don't worry it's only Canadian dollars. But for sure you could do a lot with 40 extra bytes I'm sure.
  8. By the way... Is there a reason this console ROM routine is not used by TI-Forth/FB-Forth?
  9. I looked at it but will it work for EA5 Binary format programs?
  10. I don't want to stress you out. I am just having fun making this system do tricks. Now I need to put my thinking cap on and get DSRLINK working... it's not simple. :-)
  11. Having control the of the sound chip we can now move forward to create a Musical Lexicon. This is some code that I wrote for the IBM PC internal speaker sound. With the SOUND words we created before and the Forth word MS (milli-second delay) it was simple to make this work on the TI-99. The concept here is that musical notes need a duration and an off-time to separate each note. We use music math and terminology here by setting beats per minute (BPM). This sets how long a Whole-note will last in mS. A whole note fills an entire "measure" or Bar of music. We also set a FEEL for the music which is fed to the EXPRESSION calculator and that sets the ON_TIME and OFF_TIME variables. With that all we need to do to play a note is : : PLAY ( fcode -- ) ?BREAK \ halts if function 4 pressed OSC @ OR SPLIT SND! SND! \ add osc# & write the freq code to the chip 0 ATTENUATE ON_TIME @ MS \ open the channel 15 ATTENUATE OFF_TIME @ MS ; \ close the channel You can see we check for a BREAK, then send the freq-code (fcode) to the correct Oscillator, open the channel for the ON_TIME. Then we close the channel and wait for an OFF_TIME. You may notice I don't use the words HZ and DB for control. This is because those words have overhead for human use. We pre-compute the FCODE for notes in this system because the TI-99 is not a speed demon. :-) The word ATTENUATE is like DB but uses the native values of 0 .. 15 rather than making it look nice ( 0 .. -30 DB) But they are doing the same things to the sound chip, just faster. Paste the SOUND.FTH file into your emulator first then paste in MUSIC.FTH (the spoiler code here). Then type in the examples: TWINKLE BAGPIPES VARIATION If you know a little music it's easy to program your own music. Note names go first and the system uses that note length until you change it.
  12. You caught me again! I was cleaning this file up and didn't notice that I renamed that word like that. I will fix it. For the Forth curious, Forth has what is called in academic circles as a "hyper-static" name space. Which means you can create a function, variable or whatever with the same name as something that was define before and still use the previous definition while making the new one. It's not recommended but it can be done! (as you see in my code) B *EDIT* It's gone now. Thanks Lee
  13. So in the first post we saw how to make a Ti-99 sound list player in Forth . In next few posts I will show you a few different ways that you could do this in Forth. Forth is programmed by making new words to tame the problem. One way to talk about sound is to use scientific words like Herz (Hz) and decibels (dB) The following code takes all the problems of talking to the TMS9919 chip gives you a handful of new words to make it easy. After compiling this code into the system you could make the famous TI sounds like this: : BEEP ( -- ) GEN1 1398 Hz -4 dB 170 MS MUTE ; : HONK ( -- ) GEN1 218 Hz 0 dB 170 MS MUTE ; Does this code need any explanation? Not too much. So although Forth can look very strange at the low levels, because after all it is not much higher than Assembly language at its core, with very little effort you can make into something just right for the problem at hand. Here is the code to accomplish this: In a future post we will talk about controlling the sound chip with musical words rather than scientific words.
  14. If debugging is the art of removing bugs, then programming must be the art of putting bugs in. :-) It's a humbling activity, this. B
  15. Will reviewing a site that had Forth running in a browser I found the source code for the snake game. I thought it be it useful for people learning Forth so I modified it to run on the TI-99 and added some features. This game can be pasted into CAMEL99 Forth. Download the current program in EA5 format file here: https://github.com/bfox9900/CAMEL99/blob/master/bin/CAMEL99 Run the CAMEL99 program and then paste the source code from the spoiler below into the Classic99 emulator window and type RUN <enter> For Turbo Forth and FB Forth users, it would not be a very difficult job to port it over but there might be a few questions that I am sure Mark, Lee or myself would be happy to respond to. (Note: CAMEL99 uses TI BASIC color values so they are off by 1 for FB-Forth and Turbo Forth)
  16. Ok. Well I am changing my name to ?Terminal to match yours. It was what I always used in old DOS Forths too. I am removing my ^C? Word too. It was slower and bigger and not very TI99ish.
  17. I was reading on the keyboard on the Nouspikel site and found this little gem that I had never noticed before. *--------------------------------------------- * Calling the built-in Fctn-4 test in console ROM *--------------------------------------------- TESTF4 BL @>0020 R12 will be modified JEQ CLEAR Fctn-4 was pressed JNE NOCLR It wasn't I coded up a version for my Forth system to time it. It puts a true/false flag in R4 and it only takes 170 micro-seconds, measured using the 9901 internal timer. Wow! that is a much better way to interact with the user while in a loop than anything I have coded and it's almost 7X faster than KSCAN. I didn't look at FB-Forth code yet, but maybe that's how the ?TERMINAL routine is coded? CODE: FNCT4? ( -- ?) \ 170uS 16 bit ROM speed TOS PUSH, TOS CLR, 0020 @@ BL, \ R12 will be modified @@1 JNE, \ It wasn't pressed TOS SETO, @@1: NEXT, END-CODE
  18. LOL! And the proof of that is summed up in a statement made by my late uncle Dennis who once said: "You're never as think as some drunkle peep you are!"
  19. This sounds like something that would be useful for this: http://atariage.com/forums/topic/270993-tms-9901-to-ide-interface/
  20. I created a multi-tasking context switch using the RTWP instruction. It's a pretty powerful instruction when used "backwards" this way. Something that is worth considering for parameter passing is blending a stack with a register or two. If you assign 1 register to the job of holding the TOP of stack like a little cache, then the stack becomes more efficient. You can go with 2 cached registers for top and 2nd item, but that can result in more register pushing and popping than it's worth. Another method that I have not fully explored would use a stack but then exploit BLWP to move the workspace to the top of the stack space. This would let you push parameters onto a stack as outputs of a routine for example and then process them with register instructions. I have not worked out the dynamics of this in detail, but it is something that the 9900 can do. It would require that the stack grows upward I believe so that you can preserve R13,R14,R15 above the stack. I must confess that I find overlapping register sets more complicated to think about than a stack, but that could be and Intel bias. :-)
  21. TheBF


    Me too cuz it's getting close to dinner time where I am. :-)
  22. I have not used the Pascal system on TI but I am wondering if the documentation explains stack usage per process? Could it be that the stack size is set somewhere at start up and its a shared stack space that gets consumed in chunks by each process? This might leave your print code with nothing left to format a number to text. Full on conjecture here.
  23. Ok, so I see some cool stuff in languages like Python and LISP where you can chop up a string in many sub-strings all at once. I wondered can I used this fast string cutting method to do the job. The method I used is borrowed (stolen) from an old Forth word called -TRAILING. This word takes an [address,length] pair as input and is scans backwards on the string looking for blank characters (ie: spaces ASCII 32) . If it finds a space, it shortens the string length and keeps going until it finds a non-blank character. Simple. So I created a routine that does that and I created the opposite. Meaning scan backwards on a string, ignoring non-spaces until you find a space. I call that one -ALPHA With those 2 routines we combine them to create /WORD ("cut word") This routine cuts a word off the end of a string delimited by blanks but it also re-sizes the remainder string. It uses some ugly stack gymnastics to calculate the new string lengths for both strings so unless you are really bored just assume it works. Armed with /WORD we can put this in a loop call SPLITSTR. This word cuts off a string starting from the end and leaves each sub-string on the stack as an [address,len] pair It also counts how many times it does this until the last word is found and puts that count on the top of the stack. So we are left with a variable number of strings on the stack with a count on top. The code is in the spoiler and the GIF lets you see how quickly we can get the results and even print them reversed.
  • Create New...