Jump to content


+AtariAge Subscriber
  • Content Count

  • Joined

  • Last visited

Everything posted by TheBF

  1. I have found a much better timer method on the TI-99 for a cooperative multi-tasker. I was using the 9901 timer before and waiting for it to count down for 1 milli-second. That is not ideal because the entire system comes to a stop while waiting. The ideal is you run the switcher routine whenever you have to wait, but TMS9900 is too slow to jump to another task and get back in 1mS. So.... Since the interrupts are spinning a number at >8379 all the time, I can get an accurate 1/60 of a second measurement any time I want. And in 1/60 of a second I can service some other tasks. It's faster in a 16 bit Forth to read an integer so we will read >8378 which gives use the value at 8379 without playing with the bytes. Much faster. So the method is: read the value at 8378 & keep a copy on the stack Then switch to another task When you come back, read the 8378 again Goto 1, until it's not equal to the copy Here is how it looks in Forth : [email protected] ( -- n ) 8378 @ ; \ read ISR counter : 1/60 ( -- ) [email protected] \ read the timer onto stack BEGIN PAUSE \ service other tasks. DUP [email protected] \ copy 1st reading & get a new one <> UNTIL \ loop until not equal DROP ; \ DROP initial timer reading The overall effect is a much snappier system, because it's not wasting time while doing timing delays.
  2. For those running on Windows, Context is a nice editor written in Delphi. (I don't believe source code is available) It has a simple way to create highlighter files for any language, with the common languages already provided which you can use as examples. I use it for Forth on Windows 10 64 and it's pretty nice. http://www.contexteditor.org/index.php Oh and it's freeware
  3. I thought it would be fun to try to run 6 tasks on the poor old TI-99. It actually worked. I created some tools to monitor the tasks. .TASKS command shows the Process ID, the Parameter stack, the return stack, the instruction pointer of the task and the awake/asleep status. I am closer to improving KSCAN for multi-tasking, but the code took me past the 8K file size limit of my cross-compiler, so that's the next improvement. The MULTICAM EA5 program is on GITHUB and so it the MTASKDEM.FTH file in the \LIB folder on GITHUB. https://github.com/bfox9900/CAMEL99 So you can play with this if you are interested. I will get around to doing a demo version of the DENILE program one day. It really changes the way you do games when you can assign moving pieces to a separate task. A movie of the 6 tasks test is attached. TI99 6TASKS.mov
  4. lol. So I wrote up the little routine to scan the keyboard hardware directly. Got it working. And it did what it was supposed to. Allow the multi-tasking programs to have more time to run because we only entered GPL space if a key was pressed. Nice. BUT... Classic99 PASTE function stops working because KSCAN is how it is getting characters into the emulator. Oh well. it was a good exercise. So basically if you write a game with this system you get maximum speed by putting the console to sleep until you need it. I have considered putting the YIELD routine on the ISR timer, but then you have all the problems of share resources and record locking etc. Chuck's way eliminates that but it means you have to manage time allocation yourself. That's the Forth way. I need to get my sprites code working now. I want to replace ISR driven sprite motion with the multi-tasker. We shall see how it works. "Uthe the Forth Luke" said Obiwan after his trip to the dentist.
  5. It's a daring project. You might want to consider having programs for your O/S run as byte code rather than machine code, with the ability to call machine code when needed. Byte code systems can save a lot of space fro your programs if you build an efficient virtual machine. GPL is an example, although not one I would recommend. No sense constraining yourself to 256 bytes of RAM. Basically you build a virtual machine that is core of everything and it just interprets program bytes very fast. Your virtual machine can be register based or stack based or 2 stack based like GPL, but generally is made to be simpler than the underlying machine. Any language for your O/S then just has to create byte code. P-code systems work this way, but the TI-99 version that interpreted P-code bytes with GPL byte code was a bad decision.
  6. As we say in English: "Ain't that the truth" (echt wahr)
  7. Here is the silly little demo program. Multi tasking demo.mov
  8. Oh yes there are some beautiful instructions sets and architectures out there. MIPs is among them. Sun Sparc also comes to mind with a circular set of registers on chip. Forth guys also love the RTX2000 designed by Chuck Moore that performed more that 1 instruction per clock cycle, did sub-routines calls in 1 instruction and performed sub-routine returns for in 0 clock cycles. Honest. I believe the chip was specced at 12 MIPs with a 10 MHz clock. Pretty slick. You can see something similar in Jim Bowmans FPGA J1 processor written in 200 lines of Verilog code. B
  9. Yup. That's the idea. The one problem with the current implementation is KSCAN. That darn thing takes about .7 milliseconds to run. I am told there are debouncing delays in the code and it they were implemented for a coopertative tasker, they would let other tasks work while waiting. So I found some faster "check if any key is pressed" code that I will use to speed up my KEY() function.
  10. Ya for sure the 9900 has lots of warts. It's not just slow, it's glacial. The little MSP430 addresses most of these concerns from what I can see. In a Camel99 Forth I manage 2 stacks and to prevent my stupid mistakes, I just made some macros called PUSH, POP, and RPUSH, RPOP, for the return stack. In the case of popping a stack the 9900 does it with 1 instruction so that's pretty good. For pushing it takes 2 instructions. B
  11. LOL. Ya this to me is one of the most fun things about little Forth systems. Somehow it got lost with some of the public domain systems that came out like the Forth Interest Group (FIG) . It might have been to avoid patent infringement or something like that. But ya you can see quickly how some game stuff gets a h_ll of a lot simpler if you can give it to a little daemon and forget about it. Since you are interested here is the code with a lot of comments. I am happy to answer questions... after I get some sleep.
  12. I finally got around to getting my TI-99 multi-tasker working pretty much the way I wanted. Traditionally commercial Forth systems were multi-tasking multi-user systems. I am told Forth Inc. could strap 12 terminals to an IBM PC and have good response for the users. So why is the TMS9900 so cool? because it can create a new set of registers for itself anywhere in memory and change to that set of registers in 1 instruction! For those who have never thought about it a conventional multi-tasking systems usea a special program called a scheduler that decides which program is going run and how long they get to do something. At some point while program A is running, the schedule program interrupts program A and gives program B a turn and so on with all the programs that are in the "schedule". So with only 1 CPU, it's really an illusion of multiple programs running at the same time. The Chuck Moore's Forth multi-tasker was built to be very lightweight and has not got a separate program to schedule which task is going to run and when. (It's heresy, I know) Instead a new task gets a turn EVERY time an input or output occurs. So after outputting a character to the printer another task gets a turn. If that task reads a key stroke, it releases control to the next task and so on... This works well because most I/O takes tons of time so the CPU might as well go do something else. So to make this kind of multi-tasker on the TMS9900 you can do a switch from one program to the next program in only four, yes that's right 4 instructions. This is unheard of. Below is the code for the word YIELD, which changes control to the next task, written in a Forth Assembler language. I did something with this that might be unique. I use the RTWP, instruction to jump to the next task in a list of tasks. I can do that because I manually initialize all the registers in each task's workspace as if each task was already called once by BLWP. It makes the actual switch to a new program 1 instruction. Then the only thing we do is check a variable that is right after the workspace to see if the task is awake or asleep. If it's asleep we just jump to the next workspace and so on. I love this processor! CODE: YIELD ( -- ) BEGIN, \ CURRENT TASK: RTWP, \ one instruction switches context 14 l: _TSTAT R1 STWP, \ NEXT TASK: store NEW workspace in R1 8 32 (R1) R0 MOV, \ Read local TFLAG to see if I am awake 28 NE UNTIL, \ loop thru tasks until TFLAG is <> 0 10 NEXT, \ if task is awake, run next \ 60 *.333 = 20uS END-CODE So when all is said and done I have a set of words that lets me create tasks and run them, stop them or assign them new programs almost like Unix system, but much tinier. Here is the DEMO program that I tested it with. I will get a Video up here shortly and the multi-tasking kernel up on git hub. This can be ported to FBForth or Turbo Forth with just a little assembler code but mostly high level Forth. \ CAMEL99 Forth Multi-tasking Demo \ paste into system with mtask99.hsf installed INIT-MULTI CREATE TASK1 USIZE ALLOT CREATE TASK2 USIZE ALLOT CREATE TASK3 USIZE ALLOT TASK1 FORK TASK2 FORK TASK3 FORK DECIMAL VARIABLE SPEED 25 SPEED ! : JOB1 BEGIN 15 3 DO I SCREEN ( change screen color) SPEED @ 5 MAX MS LOOP AGAIN ; : JOB2 BEGIN 90 65 DO 30 1 I 47 VCHAR 25 MS LOOP AGAIN ; VARIABLE X \ run for a period of time then go to sleep : JOB3 X OFF 2000 MS X ON MYSELF SLEEP \ easy to I am all done PAUSE ; ' JOB1 TASK1 ASSIGN ' JOB2 TASK2 ASSIGN ' JOB3 TASK3 ASSIGN With that code loaded, you type MULTI TASK1 WAKE TASK2 WAKE TASK3 WAKE TASK1 SLEEP etc.. And the Forth console is still alive the whole time... unless you say MYSELF SLEEP. :-) Nighty night Forth.
  13. As Lee stated, the variables are in the common memory space called the dictionary. <ADVANCED TOPIC> But just like in Assembler, Forth variables are just an address. ​ie: they return an address to the stack not the contents. The dictionary name is the label, but it's in a linked list of other labels. However you could use any address you cared to as a variable So you could create an array manually like this: \ purely academic example CREATE MYDATA 100 CELLS ALLOT \ now create a "calculator" to return the address of addr[x] : [] ( n addr -- addr[n] ) SWAP 1 CELLS * + ; \ usage: 99 7 MYDATA [] ! \ store 99 into cell 7 7 MYDATA [] @ \ return the contents of cell 7 </ADVANCED TOPIC>
  14. In another post we read about a person who in interested in translating a game from BASIC to Turbo Forth ... "But I do define and use a lot of variables. So I'll be exploring ways to eliminate the need to do so." There is no law of the universe that says you cannot write Forth programs and use variables just like you would in BASIC. Variables in FORTH, as in BASIC are GLOBAL. Any part of the program can get at them if it needs to. So go ahead, write a Forth program using all the variables you used in BASIC. It will work but there will be side-effects: 1. You will use some extra RAM for each variable and it's name 2. It will be more difficult to make your program connect together like Lego blocks Variables are always needed to record something the program wants to keep a permanent record of. Like say, the Score in the game needs to be recorded, and maybe the highest score I have gotten in the session. In fact Forth systems have a large number of variables built in to keep track of internal things. For example here is a partial list from CAMEL99 Forth so you can see some things that need to be kept in a variable (these may be different in other Forth systems) ( also notice that any character except space, can be used to name a variable) TFLAG \ TASK flag awake/asleep status JOB \ Forth word that runs in a task DP \ end of dictionary pointer HP \ used in number to text conversion CSP \ remembers Current Stack Position (error checking) BASE \ the current number base (decimal/hex/binary/octal) >IN \ interpreter pointer VADR \ holds VDP RAM address of the cursor C/L \ Chars per line (32 or 40 depending on VDP mode) VROW \ current VDP row VCOL \ current VDP column C/SCR \ chars per screen >300 or 3C0 VMODE \ keeps track of the video mode we are in VTOP \ top of video screen memory. defaults to 0 VBOTM \ "bottom" VDP row address for current MODE (HEX 300 or 3C0) So you can see that variables have a place in Forth. Then what's the stack for? What if all sub-routines had a secret place where they could dump their results and other sub-routines just knew where that secret place was and picked up what they needed from there? That's where the Forth stack is used. Example: Forth has a simple word that prints 1 character called EMIT. Emit knows to look on the top of the stack to get a character to print out. No variable required. In Forth we have a way to describe that with "stack comments". The stack comments for EMIT look like this EMIT ( char -- ) This means EMIT takes 1 char off the stack and puts nothing back. GCHAR is in most TI-99 Forths as well. The stack comments for GCHAR are: GCHAR ( y x -- char) GCHAR needs 2 inputs and gives back 1 character on the stack. Here is where the Lego blocks start to happen. if you wanted to read a character from the screen and re-print it at the cursor position you can feed the output of GCHAR directly into EMIT like this. 3 5 GCHAR EMIT Using the stack lets these words plug together like lego blocks. See the attached screen capture where we used HCHAR to put characters on the screen and then read them and printed them at the cursor position.
  15. My 2 cents for Sinpahltimus is to get a text editor or word processor and try writing what the different parts of the game do in your own words. Try not to think in basic, but chop the game up into the different sections of code that need to do specific stuff and say it in English. The secret to good Forth programs is very well demonstrated by Willsy's Primer #3. It takes some practice to stop thinking the way that BASIC forces us to think. For example I re-did a version of the denile program the way I would do it and here is the main routine called RUN: : RUN ( -- ) 2 SCREEN CHANGE-CHARS CLEAR .PYRAMID .STARS .MOON .RIVER HONK RESTORE-CHARSET ; 2 SCREEN is CALL SCREEN(2). So you know I changed it to a night scene. The Forth word '.' (dot) prints a number so some people put a DOT in front of words that put stuff on the screen. Now you understand what this program does pretty much right away. And it's because it was "factored" into small pieces. Oh and I had to add the stars and a moon in a night scene. :-) And the .RIVER word is actually a loop that does all the action with the water and star twinkling and exits if you hit a key. It looks like this: Hope this helps. : .RIVER ( -- ) INIT-WAVES BEGIN 100 MS .WAVES 3 TWINKLES KEY? UNTIL ;
  16. Nicely done Willsy. You covered a lot of ground there. And I realize I need to get the new version. It looks nicer. What are fast variables?
  17. Master Yoda says: Beware of the Forth must you be. For soon backing talkwards will you be!
  18. Cool so these are 64 bit math operations. I just may re-code these in Forth Assembler and give them a try. Thanks!
  19. I wondered why you were not here to correct all my mistakes. :-) So happy that you are back. Get back to 110% soon Lee. You are in my thoughts. B
  20. Had to step away. Yes I believe that's it in a nut shell. And with 32bit math you can build a financial package that 's good to 21 474 836 pounds sterling and 47 P or so. :-)
  21. I think it should be pretty straightforward with all that. It really is just creating the output operators for the fixed size you want to use isn't it? Or am I missing something? All the maths will work out of the box. I suppose you could "tart" things up with a special vocabulary or even an infix parser. Depends what you want I guess. B
  22. Hey Mark, I think you already have a fixed point library! 299.99 fixed point is still just 29999 internally. What Forth does is provides a set of words to format any number as fixed point numbers. First you need this little word set. Believe it or not this allows arbitrary number formatting in 114 bytes of code on a 16 bit machine. ( All these routines assume a 32 bit number on the stack (ie: 2 16 bit ints) which makes them much more practical for money programs. From CAMEL99 source code : UD/MOD ( ud1 u2 -- u3 ud4) >R 0 [email protected] UM/MOD -ROT R> UM/MOD ROT ; \ 32/16->32 divide : HOLD ( char -- ) -1 HP +! HP @ C! ; \ decr. HOLD pointer HP, Store char at the address contained in HP \ : >DIGIT ( n -- c) DUP 9 > 7 AND + 30 + ; \ convert n to ascii digit c (*Moved to ASM word because it is in a loop) : <# ( --) #PAD HP ! ; \ initialize Hold Pointer to end of the number buffer (#pad) : # ( ud1 -- ud2) BASE @ UD/MOD ROT >DIGIT HOLD ; \ convert 1 digit & store at HP, return remainder : #S ( ud1 -- ud2) BEGIN # 2DUP OR 0= UNTIL ; \ convert all digits in ud1. ud2 will be 0 (the remainder) : #> ( ud1 -- c-addr u) 2DROP HP @ #PAD OVER - ; \ return a stack string (address, length) of the converted number : SIGN ( n -- ) 0< IF [CHAR] - HOLD THEN ; \ if 'n'<0 add '-' char string created in #PAD Then you use these routines to make your number formats. The simplest is below; : U. ( u -- ) S>D <# #S #> TYPE SPACE ; \ print 'u' as an un-signed integer But here is signed dollar formatting \ From CAMEL Forth source code Brad Rodriguez : DNEGATE ( d -- d) SWAP INVERT SWAP INVERT 1 M+ ; : ?DNEGATE ( d1 n -- d2) 0< IF DNEGATE THEN ; \ negate d1 if n negative : DABS ( d1 -- +d2 ) DUP ?DNEGATE ; \ absolute value dbl.prec. CHAR . CONSTANT '.' \ from STARTING Forth, Brodie : .$ ( n -- ) S>D TUCK DABS <# # # '.' HOLD #S ROT SIGN [CHAR] $ HOLD #> TYPE SPACE ; Works like this: 29950 .$ $299.50 -30095 .$ $-300.95
  23. Many of the old Forth systems resorted to a 32 bit library for fixed point math to prevent overflow. I have never thought about creating a 16 bit version. I would be useful for scientific applications if you were data logging low voltages and currents I suppose. Interesting... The creator of Forth made a couple of operators to help with 16bit overflow in calculations. One is '*/' called "star-slash". Arguments are ( n, multiplier, divisor ) and it returns 16bit int. You use it to scale values that could overflow on the multiply operation because the mpy is mixed math producing a 32 bit product The other handy one is called M+ which takes 2 ints and returns a 32bit int. B
  24. This is very cool. Thank you. I am going to use this to debug my SPRITE CODE. BF
  25. OK I promise this is the last one for Primer #2. Now we have removed all the BASIC comments to make space on the page. With that space we have shown you some alternative ways to format Forth code to make it read nicely. Forth source code is free form so you can express yourself in whatever way works for you. We also introduce: COLORS ( set# set# FG,BG -- ) SET# ( char -- set$) VREAD ( vdp-addr cpu-addr cnt -- ) (also known as VMBR) VWRITE ( cpu-addr vdp-addr cnt -- ) ( a.k.a VMBW ) With those words we create a way to save the all the patterns and restore them when the program ends. And we include a little TI BASIC "HONK" so it's official.
  • Create New...