-
Content Count
3,166 -
Joined
-
Last visited
Posts posted by TheBF
-
-
4 hours ago, Willsy said:Nice. I think TF does something similar? From memory, TF puts the address to write the value stack in line with a word called doTO.
10 TO FRED
Might nievely compile to:
LIT 10 LIT FRED !
TF compiles it as LIT 10 doTo FRED
One less run through the inner interpreter.
Sounds the same yes. In my case Store is slower when you cache TOS in a register so I really win using the extra register instead of the stack.
But in the end it really is about removing cycles through the inner interpreter if you want to go faster.
I have broken my DTC version somewhere in the branching mechanism. I don't think DTC is very practical for TI-99 because of size, but it it is certainly fun to watch it run with a two instruction NEXT.
*IP+ W MOV,
*W B,
And the way I did it R11 becomes the W register which is pretty neat.
I started reviewing something I call ASM Forth that I started but didn't finish. When I can see that it works well enough, I will put it up on Github. We might have to start another topic for that one.
-
4
-
-
I am always amazed how a small change can make a difference.
So I was testing my latest kernel build and I found the Benchie benchmark.
It assigns a VALUE in the middle of the loop.
I was getting a timing of 26.25 seconds whereas TurboForth could rip this off in 24.5 seconds.
5 CONSTANT FIVE 0 VALUE BVAR HEX 100 CONSTANT MASK : BENCHIE MASK 0 DO 1 BEGIN DUP SWAP DUP ROT DROP 1 AND IF FIVE + ELSE 1- THEN TO BVAR BVAR DUP MASK AND UNTIL DROP LOOP ;
I wondered if assigning that VALUE with TO was the problem. My TO code just used LITERAL and !. Just bog standard Forth.
Seemed like a long shot to me but what the heck.
So I did this and created a literal operator that did the store and removes 2 instructions to push the stack down by using R1 for the address.
CODE LIT! ( n addr -- ) \ combine function of LIT and ! *IP+ R1 MOV, TOS R1 ** MOV, TOS POP, NEXT, ENDCODE .( .) : VALUE CONSTANT ; : TO ( n -- ) ' >BODY \ compute PFA at compile time STATE @ IF POSTPONE LIT! , EXIT THEN ! ; IMMEDIATE
And... just like that it's 24.5 seconds.
-
4
-
-
3 hours ago, Bill R Sullivan said:This reply is mainly to Lee and "The BF", as their replies have made me realize just how much I would have to do to make all my established methods work in fbForth. Plus the fact that I will also be using these methods for my SGCPU in my SNUG TI-99/4P system which has a 16 bit 1MB SAMS! My 80 year old brain can't deal with this change no matter how much help you guys give me. I even considered just retiring from my TI hobby, but then considering all the time I spend in my now tiny "Man Cave" due to COVID19 lock downs I would surely end up in the psycho ward for the duration of my life. I can only apologize for taking up your time and diverting the mainline conversation for my sake. I will just go to my corner and think only X4th99, as it is only intended to keep my mind functional during these hectic times of the Corona Virus and it's mutations!
Extremely crazy old guy,
RetroBill (FDOS)
I sure get the challenge of fitting Forth code from one system into another. It ain't easy.
No need to apologize. We all are doing the same thing here. Just helping each other along.
But please carry on with your Forth and if there is anytime you say "I wonder if the other guys have an idea for this", Lee and I enjoy that kind of discussion.
Keep the Forth
-
Not sure if anyone here can help with this.
Found on Hacker News.
Atari asks gamers for help finding developer of mysterious 2600 game | VentureBeat
-
2 hours ago, Bill R Sullivan said:Lee, truly amazing! Maybe I should consider dropping X4th99 development and just use fbForth, provided I can easily learn how to use your memory manager!
RetroBill (FDOS)
I have some useful structures too Bill that could be bolted onto FbForth.
On is like BLOCK but it's SAMS blocks.
SBLOCK ( n -- addr)
Pick the SAMS page and it pulls it in and returns the address where it lives.
An SBLOCK is 4K. There are 2 alternating buffers that live in a consecutive 8K space.
You can put them where you what in memory with one constant in the code.
But with 2 buffers you can do SAMS to SAMS transfers easier.
This is in Forth assembler so it would need a few tweaks for FbForth.
But it could also be made with >MAP in Forth and it would still be way faster than BLOCK.
And then there is PAGED ( virtual-addr -- real-addr)
You set a 64K segment of SAMS memory with: HEX 10 SEGMENT
Now you have a 64k address by adding PAGED to your memory operations.
: @S ( addr -- n ) PAGE @ ; : !S ( n addr -- ) PAGE ! ; etc...
PAGED would not be hard to port to FbForth I think. Lee and I just have not tried.
So lots of possibilities with the gang here.
Spoiler\ SAMS CARD support for CAMEL99 in Forth May 2020 B Fox \ 16 by 64K segmented memory model for DATA \ You can access any ADDRESS from 1 to 65535 using PAGED NEEDS SAMSINI FROM DSK1.SAMSINI HERE VARIABLE SEG \ holds current 64K segment VARIABLE BANK# \ current mapped bank HEX 3000 CONSTANT DMEM \ CPU RAM memory block location \ Legal values: 2000,3000,A000,B000,C000,D000,E000,F000 \ compute SAMS register based on PMEM address DMEM 0B RSHIFT 4000 + CONSTANT DREG HEX : PAGED ( virtual-addr -- real-addr) SEG @ 1000 UM/MOD ( -- offset bank#) DUP BANK# @ - \ different page? IF DUP BANK# ! SAMSCARD 0SBO \ card on, enable registers ( bank#) >< DREG ! \ swap bytes & store in SAMS register 0SBZ \ card off ELSE DROP THEN DMEM + \ then add offset to paged mem block ; \ safely set the 64K segment that you want to use : SEGMENT ( 1..F -- ) \ don't allow segment 0 DUP 01 10 WITHIN 0= ABORT" SAMS segment err" SEG ! ; CR HERE SWAP - DECIMAL . .( bytes) SAMS-OFF SAMSINI SAMS-ON 1 SEGMENT CR .( SAMS card activated) CR .( Window = ) DMEM HEX U. CR .( Segment = ) SEG @ DECIMAL U. CR
-
4
-
-
In my obsessive way I took this game and used it to learn how to make my system work better.
I never wrote games so it gave me a good skeleton and poked holes in my system.
It is also politically incorrect since my snake likes to eat mice not apples.
oops!
Anyway here is the source code after 6 interations and blowing up my underlying code.
I must confess that it does get a bit addictive trying to go at "Viper" speed.
It doesn't have a remembered scoring system so it's still not a finished product.
I attached an executable program for the E/A cartridge option 5.
Spoiler\ snake a simple game in Forth ported to CAMEL99 Forth \ DERIVED FROM: https://skilldrick.github.io/easyforth/#snake \ revised to use CAMEL99/TI-99 features CR .( \\\\\\ Version 6 \\\\\\\\ ) \ \\ snake sounds and mouse squeak \\\\\ INCLUDE DSK1.RANDOM INCLUDE DSK1.GRAFIX INCLUDE DSK1.CASE INCLUDE DSK1.ARRAYS INCLUDE DSK1.UDOTR INCLUDE DSK1.MARKER CR .( compiling Snake...) \ ======================================= \ We use direct control of the sound chip \ rather than sound lists and a player. HEX \ noise control words : NOISE ( n -- ) E0 OR SND! ; \ n selects the noise type \ noise envelope control : NOISE-DB ( db --) F MIN F0 OR SND! ; : NOISE-OFF ( -- ) F NOISE-DB ; DECIMAL : NOISE-UP ( speed -- ) 2 15 DO I NOISE-DB DUP MS -1 +LOOP DROP ; : NOISE-DOWN ( speed -- ) 15 2 DO I NOISE-DB DUP MS LOOP DROP NOISE-OFF ; \ channel 1 sound control words DECIMAL : f(clk) ( -- d) 46324 1 ; \ this is 111,860 as 32 bit int. \ >FCODE re-arranges freq. value nibbles (4bits) for the TMS9919 HEX : >FCODE ( 0abc -- 0cab) \ ASM would make this much faster DUP 0F AND SWAP ( -- 000c 0abc) 4 RSHIFT ( -- 000c 00ab) SWAP >< ( SWPB) ( -- 00ab 0c00) + ; : HZ>CODE ( freq -- fcode ) f(clk) ROT UM/MOD NIP >FCODE 8000 OR ; \ *TRICKY STUFF* \ Calculating the 9919 freq. code takes too long BUT we can convert frequency \ to 9919 chip code at compile time then compile as 16 bit literal number \ using this text MACRO : [HZ] ( freq -- fcode ) S" HZ>CODE ] LITERAL" EVALUATE ; \ sound channel #1 control words : FREQ! ( fcode -- ) SPLIT SND! SND! ; : ]HZ ( freq -- ) [HZ] POSTPONE FREQ! ; \ pre-compiled fcode version : HZ ( freq -- ) HZ>CODE SPLIT SND! SND! ; \ runtime calculation version : DB ( n -- ) 90 OR SND! ; : MUTE ( -- ) 9F SND! ; DECIMAL 500 CONSTANT MAXLENGTH \ x/y coordinate storage for the snake MAXLENGTH 2+ ARRAY ]SNAKE-X MAXLENGTH 2+ ARRAY ]SNAKE-Y : SNAKE-X-HEAD ( -- addr) [ 0 ]SNAKE-X ] LITERAL ; : SNAKE-Y-HEAD ( -- addr) [ 0 ]SNAKE-Y ] LITERAL ; .( .) VARIABLE SPEED VARIABLE PREY-X VARIABLE PREY-Y VARIABLE DIRECTION VARIABLE LENGTH \ characters used DECIMAL 136 CONSTANT SHEAD \ use different color set 42 CONSTANT SNAKE 128 CONSTANT PREY 30 CONSTANT BRICK BL CONSTANT WHITE \ Direction #s 0 CONSTANT LEFT 1 CONSTANT UP 2 CONSTANT RIGHT 3 CONSTANT DOWN HEX CREATE HEADLEFT 0C16 , 37FF , FF37 , 160C , CREATE HEADUP 1818 , 3C7E , 99FF , 7E3C , CREATE HEADRIGHT 3068 , ECFF , FFEC , 6830 , CREATE HEADDOWN 3C7E , FF99 , 7E3C , 1818 , \ array of head patterns CREATE HEADS ( n -- addr ) HEADLEFT , HEADUP , HEADRIGHT , HEADDOWN , \ set head pattern n to snake's head : ]HEADPATTERN ( n -- addr) CELLS HEADS + @ SHEAD CHARDEF ; : FACE ( direction# -- ) DUP ]HEADPATTERN DIRECTION ! ; \ shape data for PREY, brick, mouse and snake chars HEX CREATE CLAY 007E , 6A56 , 6A56 , 7E00 , CREATE VIPER 3C5E , EBF7 , EBDD , 7E3C , CREATE MOUSE 0004 , 3E7B , 7FFC , 8270 , CREATE MOUSE2 0008 , 3F7B , 7EFC , 8270 , \ mouse looking up CREATE JUMPMS 84BE , FB7F , 3C42 , 0000 , DECIMAL \ get random x or y position within playable area : RANDOM-X ( -- n ) C/[email protected] 2- RND 1+ ; : RANDOM-Y ( -- n ) L/SCR 2- RND 1+ ; \ text macros make drawing faster. : DRAW-WHITE ( x y -- ) S" >VPOS BL SWAP VC! " EVALUATE ; IMMEDIATE : DRAW-SNAKE ( X Y -- ) S" >VPOS SNAKE SWAP VC! " EVALUATE ; IMMEDIATE : DRAW-HEAD ( x y -- ) S" >VPOS SHEAD SWAP VC! " EVALUATE ; IMMEDIATE : DRAW-PREY ( -- ) PREY PREY-X @ PREY-Y @ >VPOS VC! ; .( .) : DRAW-WALLS 0 0 BRICK 31 HCHAR 0 1 BRICK 22 VCHAR 31 0 BRICK 24 VCHAR 0 23 BRICK 31 HCHAR ; : DRAW-SNAKE SNAKE-X-HEAD @ SNAKE-Y-HEAD @ DRAW-HEAD LENGTH @ 1 DO I ]SNAKE-X @ I ]SNAKE-Y @ DRAW-SNAKE LOOP LENGTH @ DUP ]SNAKE-X @ SWAP ]SNAKE-Y @ DRAW-WHITE ; : INITIALIZE-SNAKE 6 DUP LENGTH ! 1+ 0 DO 12 I - I ]SNAKE-X ! 12 I ]SNAKE-Y ! LOOP RIGHT FACE ; : PLACE-PREY ( y x -- ) PREY-X ! PREY-Y ! ; : MOVE-SNAKE-HEAD ( n -- ) DIRECTION @ CASE LEFT OF -1 SNAKE-X-HEAD +! ENDOF UP OF -1 SNAKE-Y-HEAD +! ENDOF RIGHT OF 1 SNAKE-X-HEAD +! ENDOF DOWN OF 1 SNAKE-Y-HEAD +! ENDOF ENDCASE ; \ move each segment of the snake forward by one DECIMAL \ : MOVE-SNAKE-TAIL \ 0 LENGTH @ \ DO \ I ]SNAKE-X DUP @ SWAP CELL+ ! \ I ]SNAKE-Y DUP @ SWAP CELL+ ! \ -1 +LOOP ; : MOVE-SNAKE-TAIL \ src addr dest addr size \ --------- --------- ------------- SNAKE-X-HEAD DUP CELL+ LENGTH @ CELLS CMOVE> SNAKE-Y-HEAD DUP CELL+ LENGTH @ CELLS CMOVE> ; : MOVE-SNAKE ( -- ) MOUSE2 PREY CHARDEF 4 NOISE 8 NOISE-DB \ soft white noise MOVE-SNAKE-TAIL 10 NOISE-DB \ ramp down noise MOVE-SNAKE-HEAD 12 NOISE-DB \ ramp down noise MOUSE PREY CHARDEF NOISE-OFF ; .( .) DECIMAL : HORIZONTAL? ( -- ?) DIRECTION @ DUP LEFT = SWAP RIGHT = OR ; : VERTICAL? ( -- ?) DIRECTION @ DUP UP = SWAP DOWN = OR ; : TURN-UP HORIZONTAL? IF UP FACE THEN ; : TURN-LEFT VERTICAL? IF LEFT FACE THEN ; : TURN-DOWN HORIZONTAL? IF DOWN FACE THEN ; : TURN-RIGHT VERTICAL? IF RIGHT FACE THEN ; ( EXIT THEN gets out of the case statement faster than ENDOF) : CHANGE-DIRECTION ( key -- ) CASE [CHAR] S OF TURN-LEFT EXIT THEN \ ENDOF [CHAR] E OF TURN-UP EXIT THEN \ ENDOF [CHAR] D OF TURN-RIGHT EXIT THEN \ ENDOF [CHAR] X OF TURN-DOWN EXIT THEN \ ENDOF ENDCASE ; DECIMAL : SWOOSH ( -- ) NOISE-OFF 5 NOISE 8 NOISE-UP 20 NOISE-DOWN ; : NEW-PREY SWOOSH PREY-X @ PREY-Y @ DRAW-WHITE RANDOM-Y RANDOM-X PLACE-PREY DRAW-PREY ; : GROW-SNAKE ( -- ) 1 LENGTH +! ; : DEAD-SNAKE ( -- ) NOISE-OFF SNAKE SET# DUP 11 1 COLOR 250 MS 2 1 COLOR SHEAD SET# 2 1 COLOR ; : HAPPY-SNAKE ( -- ) [ SNAKE SET# ] LITERAL 17 3 DO DUP I 1 COLOR I 100 * HZ 0 DB 40 MS LOOP MUTE ( -- 5) 13 1 COLOR ; .( .) DECIMAL : DECAY ( n -- ) 16 0 DO I DB DUP MS LOOP DROP ; : SQUEAK ( -- ) NOISE-OFF [ 3800 ]HZ 0 DB 45 MS \ pre-computed freq. is faster 6 DB 25 MS [ 3500 ]HZ 75 MS 8 DB 25 MS [ 1300 ]HZ 11 DB 25 MS [ 800 ]HZ MUTE ; DECIMAL : SCARED-PREY ( -- ) JUMPMS PREY CHARDEF SQUEAK [ PREY SET# ] LITERAL DUP 9 1 COLOR 2 1 COLOR MOUSE PREY CHARDEF ; : FASTER ( -- ) SPEED @ 5 - 1 MAX SPEED ! ; : CHECK-PREY ( -- ) SNAKE-X-HEAD @ PREY-X @ = SNAKE-Y-HEAD @ PREY-Y @ = AND IF SCARED-PREY HAPPY-SNAKE GROW-SNAKE FASTER NEW-PREY THEN ; : COLLISION? ( -- ? ) SNAKE-X-HEAD @ SNAKE-Y-HEAD @ >VPOS [email protected] DUP BRICK = SWAP SNAKE = OR ; \ utility words for menus : WAIT-KEY BEGIN KEY? UNTIL ; : AT" POSTPONE AT-XY POSTPONE ." ; IMMEDIATE : INITIALIZE PAGE 4 SCREEN MOUSE PREY CHARDEF PREY SET# 2 1 COLOR CLAY BRICK CHARDEF BRICK SET# 9 1 COLOR VIPER SNAKE CHARDEF SNAKE SET# 13 1 COLOR SHEAD SET# 7 1 COLOR DRAW-WALLS INITIALIZE-SNAKE RANDOM-Y RANDOM-X PLACE-PREY ; .( .) HEX : PLAY ( -- ) BEGIN SPEED @ MS DRAW-SNAKE DRAW-PREY 83C8 OFF \ set continuous key reading KEY? CHANGE-DIRECTION MOVE-SNAKE CHECK-PREY COLLISION? UNTIL 0C SCREEN HONK 0B 5 AT" GAME OVER" HONK DEAD-SNAKE ; DECIMAL : ENDGAME 5 14 AT" You sure? (Y/N)" KEY [CHAR] Y = IF PAGE ABORT ELSE 5 14 >VPOS 26 BL VFILL THEN ; : SETLEVEL ( n --) SPEED ! ; : BREAK; POSTPONE EXIT POSTPONE THEN ; IMMEDIATE DECIMAL : MENU PAGE 5 5 AT" Select Start Level" 5 8 AT" 1 - SNAIL" 5 9 AT" 2 - WORM" 5 10 AT" 3 - SNAKE 5 11 AT" 4 - VIPER" 5 12 AT" 5 - Quit 3 23 AT" (It goes faster as you win)" BEGIN 5 13 AT-XY KEY CASE [CHAR] 1 OF 150 SETLEVEL BREAK; [CHAR] 2 OF 110 SETLEVEL BREAK; [CHAR] 3 OF 75 SETLEVEL BREAK; [CHAR] 4 OF 50 SETLEVEL BREAK; [CHAR] 5 OF ENDGAME ENDOF HONK ENDCASE AGAIN ; DECIMAL : TITLE ( -- ) GRAPHICS 5 5 AT" THE SNAKE" 5 7 AT" Use the E,S,D,X keys" 5 8 AT" to move the snake 5 9 AT" and catch the mouse." 5 12 AT" The more he eats, 5 13 AT" the faster he goes!" 5 20 AT" Press any key to begin..." WAIT-KEY ; .( .) : RUN ( -- ) TITLE BEGIN MENU INITIALIZE PLAY 500 MS KEY? DROP ( wait for key release) 2 13 AT" Your snake was " LENGTH @ . ." Ft. long" 2 15 AT" Press ENTER to play again" KEY 13 <> UNTIL NOISE-OFF 8 20 AT" SSSSSee you later!" 1500 MS GRAPHICS ; : COLD WARM RUN BYE ; LOCK INCLUDE DSK1.SAVESYS ' COLD SAVESYS DSK3.SNAKE6
-
3
-
3
-
-
43 minutes ago, GDMike said:My brain has to capture this meaning, after I wake up a bit.. bwhhaha...ok...thx lee for this.
You're both absolutely correct. I need to figure this out.
Some might say the stack diagram tells you the "gazintas and gazouttas"
of a word.
( what 'goes into" and what "goes out of")
-
1
-
-
This is an interesting problem.
I started playing with sorting the directory of multiple disks.
I used my Shell program as a start since it had the fundamentals already built.
The method is to read the file names into VDP RAM as a byte-counted compact list.
Starting at >1000 there is room for 947, 10 character filenames stored this way.
More room if the file names are smaller.
This could be modified to be record based and save out to disk.
To sort that array the string addresses are transferred into low RAM.
So that gives us a potential for 4K pointers
The sort does a VDP string compare (the strings never leave VDP RAM in this example
)
It's COMBSORT in Forth, so it's not overly fast.
The result of the string comparison controls if the pointer array elements are swapped.
The video shows me using the programmer API commands.
They can be humanized later.
Time for bed.
-
2
-
-
10 hours ago, apersson850 said:I once wrote, or at least attempted to write, a disk catalog program, which should be able to handle multiple floppies. Those were the only interesting storage media at that time. Here's a list of required functions:
- Should be able to read the file catalog on multiple diskettes.
- Files should be possible to list in alphabetical order, even if they were found on different diskettes.
- The list should include the name of the diskettes they were found on, and/or a reference number for the diskette.
- The reference number should remain the same for a disk, even if the name was changed.
- You were supposed to mark the label on the disk with the reference number, but the number was also stored in an unused part of the diskette catalog sector.
- If you read the same diskette once again, it was recognized from the number stored in the catalog sector. In such a case, the content for that disk was updated, not just read again completely.
- The list of files should be kept in a data file.
- It should be possible to start the catalog program at a later time, i.e. not the same day the diskettes were read, and still locate a file name, and the corresponding diskette name/number, by using the stored data from previous sessions.
- A diskette number should be possible to remove manually from the database, e.g. if the diskette was destroyed.
- The program required at least two diskette stations, one for the program and the data file with diskette names, the other for reading new diskettes.
- If the database was large, it would require the program on one drive, the database on a second and new diskettes in a third drive.
Although my basic idea worked, so that I could generate lists of files, with reference to the diskette they resided on, I found that it wasn't feasible when the number of diskettes grew large. 10, 20, maybe even 30 diskettes, well, that kind of worked. But when they grew to a hundred or more, the program became too slow to be reasonable to use. Back then, I had only the standard TI 99/4A, with 32 K memory expansion. I had neither a RAMdisk nor any other additional RAM. Handling the database simply got too slow.
I've been searching here for a while, but can't find anything similar. Has it ever been created? Does a working program of this kind exist? I'm talking about something that can run on a real TI, since emulated versions present other possibilities for overviewing the emulated diskettes.
Are all the devices starting with DSK1. DSK2. DSK3 etc. ?
Based on our work before and using VDP to hold the strings I might be able to make something like this with the tools I have at the moment.
-
56 minutes ago, Willsy said:I've entered it into TF and it compiles successfully, but how do I run it? When I execute RKEY it just leaves 13 on the stack (enter key). Any example calling code?
: TEST BEGIN RKEY DUP EMIT 90 = UNTIL ;
Is what I do.
-
4
-
-
18 minutes ago, acadiel said:Das ist ein anderer Boot.
-
2
-
-
17 hours ago, apersson850 said:As a stock feature, the p-system has a 32-bit timer that's running all the time, driven by the VDP interrupt. It does of course stop each time you access a floppy disk or similar. It can be ccessed by the intrinsic Pascal procedure time.
My real-time clock has nothing to do with that timer, though. It's implemented with a National Semiconductor MM58167A RTC chip on a card in the PEB. It's visible in some of the pictures in this album.
That's is a great looking TI-99. Thanks for sharing.
I guess if I really wanted a real time clock I should make a board for my PEB.
One day maybe. Still lots of S/W ideas to explore.
-
2
-
-
This gives me even more admiration for Apple's Rosetta emulation.
They seem to be able to emulate Intel on ARM with at most a 2X slowdown.
And they did it way back emulating Power PC on Intel.
Makes me wonder how.
I know the 9900 is not like normal machines but it seems like it is a real challenge for ARM to emulate based on your results.
I wonder if Apple is using something like a huge jump table (because they have the RAM) so that instructions compute the address of the emulation code. ??
Way above my pay grade.
-
2
-
-
59 minutes ago, GDMike said:Ahh. Yes. The "Begin" is what I was missing in my code, I was trying to do everything within a Do LOOP.
DO LOOPS are great for simple things.
They can reach out and byte you if you try and use the return stack for some temporary storage because they typically keep the looping information over there.
So BEGIN 0= UNTIL is a common loop. 0= of course can be any conditional you need.
Forth's WHILE loop is unlike any other language because it is split into two sections and you and use them any way you need to.
BEGIN
TRUE
WHILE
DO-SOMETHING
REPEAT
-OR-
BEGIN
DO-SOMETHING
TRUE?
WHILE REPEAT
-OR-
BEGIN
DO-SOMETHING
AND-SOMETHING-ELSE
AND-ANOTHER?
WHILE ( tos=true do)
LOTS-MORE STUFF
ETC. ETC. ETC.
THAT-THING-I-FORGOT
REPEAT
-
2
-
-
I revisited this 32 bit timer idea with fresh eyes and I think I have something that works well.
The idea is the 9901 can only time 349 mS.
The interrupt happens every 16mS but not reliably.
So...
Let's read the timer every interrupt and add it's value to a 32 bit accumulator variable.
It's not likely the interrupts will be off for 300 milli-seconds at least in my system.
That let's us keep a very big number as a timer.
It's spinning away here on the screen.
NEEDS MOV, FROM DSK1.ASM9900 NEEDS ISR' FROM DSK1.ISRSUPPORT CREATE T32 0 , 0 , \ 32 bit timer accumulator CODE TIMERISR \ read the timer, which runs continuously in Camel99 Forth 0 LIMI, R12 2 LI, -1 SBO, R0 14 STCR, -1 SBZ, 2 LIMI, \ add timer value to 32 bit accumulator R0 T32 2+ @@ ADD, OC IF, T32 @@ INC, ENDIF, RT, ENDCODE ISR' TIMERISR INSTALL
-
4
-
-
44 minutes ago, senior_falcon said:Does anyone has another font that works well in the 6x8 character size used by the text modes?
I use this one which I found here in the GIF library.
You probably already have it.
I think I tweaked the '0' and maybe a couple of letters and added a 40 column TI logo in the lower characters. (CHARS 11 to 20)
-
2
-
-
And I found the entry for this one wanting as well.
When you have something as versatile as Forth you have to show off a little bit don't you?
So I added FOREACH.
-
2
-
-
I went looking on Rosetta Code for tasks that were not completed in Forth.
This one looked like one I could accomplish so I put it up there.
-
2
-
-
6 hours ago, apersson850 said:OK. That means that your code does run with interrupt service enabled, or the timing wouldn't work. When my program is running interrupts are disabled, so it saves some fraction there. My super-duper 99 also includes a real time clock, based on a clock chip from National, with a resolution of one millisecond. I check the time before and after launching the sort procedure, and get the time from that.
I've written a library unit called realtime, which allows access to my clock. The clock is my own design, so I had to make the drivers for it, of course. When the program includes uses realtime, a timer data type becomes available. The unit supports dynamic creation and disposal of timers. When a timer has been created, it can be reset, started and stopped. And read, of course.
Here is a syntactically incorrect example, which just shows the principle of how I use them. I could stop the timer before reading it, but it's not mandatory. Any number of timers can be created and run simultaneously.
uses realtime; var timer: tmrtype; elapsed: timetype; begin new(timer); tmrreset(timer); tmrstart(timer); quicksort(n,array); tmrread(timer,elapsed); with elapsed do write(hour); write(minute); write(second); writeln(fraction); end; dispose(timer); end.
Very nice. That's the way to do it. Hardware!
I have the 9901 timer running continuously in Camel99 Forth and I use it primarily for timing the "MS" word in Forth which waits for milli-seconds.
( I limit resolution to 10mS because I poll it in Forth (slow) and it also yields to any other tasks while it's waiting)
( the code below is in my Cross-compiler Forth. That's the reason for the special incantations.
[CC] "cross-compiling" lets me use the host Forth interpreter for immediate commands
[TC] "target-compiling" All definitions goe into the target image )
\ TICKTOCK.HSF TMS9901 hardware timer interface for Camel 99 Forth \ credit to: http://www.unige.ch/medecine/nouspikel/ti99/tms9901.htm#Timer \ impovements based on code from Tursi Atariage \ TMR! now loads from the Forth stack \ Jan 31, 2021 simplified JIFFS \ Dec 2021 removed JIFFS , replaced with TICKS which gives ~10mS resolution \ timer resolution: 64 clock periods, thus 64*333 = 21.3 microseconds \ Max Duration : ($3FFF) 16383 *64*333 ns = 349.2 milliseconds CROSS-ASSEMBLING [CC] DECIMAL [TC] CODE TMR! ( n -- ) \ load TMS9901 timer from stack 0 LIMI, 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 TOS 14 LDCR, \ Load 14 BITs from TOS into timer -1 SBZ, \ reset bit 0, Exits clock mode, starts decrementer 2 LIMI, TOS POP, NEXT, ENDCODE CODE [email protected] ( -- n) \ read the TMS9901 timer 0 LIMI, TOS PUSH, R12 2 LI, \ cru = 1 (honest, 2=1) -1 SBO, \ SET bit 0 TO 1, Enter timer mode TOS 14 STCR, \ READ TIMER (14 bits) -1 SBZ, \ RESET bit 1, exit timer mode 2 LIMI, NEXT, ENDCODE [CC] DECIMAL [TC] : TICKS ( n -- ) \ n must be less than 4000. 4000 TICKS ~= 100 mS [email protected] >R BEGIN PAUSE [email protected] [email protected] - ABS OVER > UNTIL R> 2DROP ; : MS ( n -- ) 10 / 0 ?DO 420 TICKS LOOP ; [CC] HEX [TC]
I also use it for testing Assembly language code routines. Of it course it rolls over every 349 mS or so.
I explored some code last year that reads the 9901 on the VDP interrupt and adds the difference from last reading to a 32bit int. I didn't go back to it to really test it.
I should probably look into making that work as it would be a "reasonably" accurate fined grained timer.
For a time I had a an 8K RAM chip in my SuperCart that had battery backed-up clock built in. The batteries were dead or dying and it was unreliable so I took it out.
In Classic99 I could read the CLOCK file but that would be best for very long duration timing.
Your solution is the best of the lot but iI suppose part of the "fun" is trying make it work with the 1978 hardware.
(sometimes its not fun)
-
3
-
-
51 minutes ago, apersson850 said:Nice to see you got it to work!
Yes, the algorithm works better on random data. Data where almost all values are the same, except a few, or already sorted data, or reverse sorted - these take longer time to process. That has to do with how the sublists are created in these cases.
At the same time it's interesting to see the time difference due to the memory. It's true that my machine has 64 K RAM 16-bit wide, with no wait state generation. My random list was sorted in about 0.25 seconds, so about half the time. That corresponds to my earlier exterments ,where I found the computer to be roughly twice as fast, when running assembly programs where both the workspace and the code is in expansion memory, which normally is 8-bit wide, with six cycles per memory word access, vs. two cycles for my computer. My reversed list was sorted in about 1.4 seconds or so.
What do you use to do the timing of the procedure?
And finally, when it comes to "the regular rates" - don't forget where this came from! 😁
LOL. Yes indeed. I put your handle in my version of the code for that very reason. Willsy has to pay full freight!.
The timing is done using the screen timeout counter and scaling it.
It's not good if you are doing a lot of VDP actions because interrupts get stopped too frequently. But on code like this it real time tracks very well.
So the timing on Classic99 is very close to real iron from what I can measure so your numbers with your super-duper TI-99 make sense versus these.
Thanks again for the code. It was great fun. The first Assembly attempt showed 99 errors. How's that for a coincidence?
Here is the ELAPSE timer code in Forth
Spoiler\ ELAPSE.FTH elapsed time measurment words \ Thanks to Tom Zimmer for the good ideas in FPC circa 1990 \ Ported to HsForth 08MAR91 Brian Fox Canada \ Ported to CAMEL99 Nov 29 2017, \ Simplified with SEXTAL Dec 6 2018 \ Good for 9 minutes maximum duration \ *** YOU CANNOT CALL KSCAN WHILE TIMING *** HEX 83D6 CONSTANT TICKER \ screen timeout counter increments by 2 /16mS DECIMAL : SEXTAL 6 BASE ! ; : <:> [CHAR] : HOLD ; : <.> [CHAR] . HOLD ; : TIME$ ( n -- addr len) \ string output is more flexible BASE @ >R \ 100ths secs minutes 0 <# DECIMAL # # <.> # SEXTAL # <:> DECIMAL #S #> R> BASE ! ; : .ELAPSED ( -- ) TICKER @ 5 6 */ TIME$ CR ." Elapsed time =" TYPE ; : ELAPSE ( -- <text> ) 1 PARSE TICKER OFF EVALUATE .ELAPSED ;
-
2
-
-
-
2 minutes ago, Willsy said:Wow! That's awesome - I might have to steal that! Great job
Of course my regular rates apply. 🤣
Have fun. I bet you have a ton of Assembler code you could test this way. The linker as it exists can only do 8K total of code but that's a shit-ton for testing purposes.
And all the data could be in your Forth space anyway.
-
OK. Now how cool is this video.
I did some editing on the Quicksort for Pascal so it would assemble under the TI assembler.
I loaded my sorting test set.
Link the object file and run and time it from the Forth command line.
AND... I just had to change the PASCAL stack register to Camel99's stack register in the code, and the Forth stack pointer came across into the external program.
The time of 2.66 seconds slower than the original because @apersson850 's machine has 16 bit memory I believe.
This is running completely in 8 bit RAM, workspace and all.
Here is the slightly altered source file.
Spoiler* TITL "QUICKSORT FOR INTEGERS" * Procedure sorting integers in ascending order * Called from Pascal host by QSORT(A,N)* * Declared as PROCEDURE QSORT(VAR A:VECTOR* N:INTEGER)* EXTERNAL* * Vector should be an array [..] of integer* * Author: @APERSON850 on Atariage.com * Modified for TI Assembler, to be called from Camel99 Forth * 2022 Brian Fox *-------------- * Workspace registers for subroutine at START * * R0 Array base pointer * R1 Array end pointer * R2 L * R3 R * R4 I * R5 J * R6 KEY * R7 Temporary * R8 Temporary * R9 Subfile stack pointer * R10 Main stackpointer * R11 Pascal return address (P-system WS) * R12 Not used * R13 Calling program's Workspace Pointer * R14 Calling program's Program Counter * R15 Calling program's Status Register *----------------- * The actual vector in the Pascal-program could be indexed [1..n] * This routine assumes only that n indicates the number of elements, not the * last array index. DEF QSORT LIMIT EQU 16 *Quick- or Insertionsort limit * * Don't need this for Forth ** * BLWP @QSORT * AI R10,4 *Simulate pop of two words * B *R11 *Back to Pascal host QSORT DATA SORTWS,START *Transfer vector for Quicksort START MOV @12(R13),R10 *Get stackpointer from Forth's WP LI R9,ENDSTK *SUBFILE STACKPOINTER MOV *R10+,R1 * POP PARAMETER N MOV *R10+,R0 * POP ARRAY POINTER DEC R1 SLA R1,1 A R0,R1 *CALCULATE ARRAY ENDPOINT MOV R0,R2 *L:=1 MOV R1,R3 *R:=N MOV R1,R7 S R0,R7 CI R7,LIMIT JLE INSERT *FIGURE OUT IF QUICKSORT IS NEEDED MNLOOP MOV R2,R7 SRL R7,1 MOV R3,R8 SRL R8,1 A R8,R7 ANDI R7,>FFFE *R7:=INT((L+R)/2) MOV *R7,R8 MOV @2(R2),*R7 MOV R8,@2(R2) *A[(L+R)/2]:=:A[L+1] C @2(R2),*R3 JLT NOSWP1 MOV @2(R2),R8 MOV *R3,@2(R2) MOV R8,*R3 *A[L+1]:=:A[R] NOSWP1 C *R2,*R3 JLT NOSWP2 MOV *R2,R8 MOV *R3,*R2 MOV R8,*R3 *A[L]:=:A[R] NOSWP2 C @2(R2),*R2 JLT NOSWP3 MOV @2(R2),R8 MOV *R2,@2(R2) MOV R8,*R2 *A[L+1]:=:A[L] NOSWP3 MOV R2,R4 INCT R4 *I:=L+1 MOV R3,R5 *J:=R MOV *R2,R6 *KEY:=A[L] JMP INCLOP INRLOP MOV *R4,R8 *LOOP UNWRAPPING MOV *R5,*R4 MOV R8,*R5 *A[I]:=:A[J] INCLOP INCT R4 *I:=I+1 C *R4,R6 JLT INCLOP *A[I]<KEY DECLOP DECT R5 *J:=J-1 C *R5,R6 JGT DECLOP *A[J]>KEY C R4,R5 JLE INRLOP *IF I<=J THEN CONTINUE OUT MOV *R2,R8 MOV *R5,*R2 MOV R8,*R5 *A[L]:=:A[J] DEL1 MOV R5,R7 *Quicksort subfiles? S R2,R7 *R7:=J-L MOV R3,R8 S R4,R8 INCT R8 *R8:=R-I+1 CI R7,LIMIT JH DEL2 CI R8,LIMIT JH DEL2 CI R9,ENDSTK *LVL=0? JEQ INSERT *No more Quicksorting at all? MOV *R9+,R2 *POP L MOV *R9+,R3 *POP R JMP MNLOOP DEL2 C R7,R8 *Determine what is small and large subfile JL ELSE2 MOV R2,@LSFL MOV R5,@LSFR DECT @LSFR MOV R4,@SSFL MOV R3,@SSFR JMP DEL3 ELSE2 MOV R4,@LSFL MOV R3,@LSFR MOV R2,@SSFL MOV R5,@SSFR DECT @SSFR DEL3 CI R7,LIMIT *Is small subfile big enough to be sorted by JLE THEN3 *Quicksort? CI R8,LIMIT JH ELSE3 THEN3 MOV @LSFL,R2 *Don't Quicksort small subfile, only large MOV @LSFR,R3 JMP MNLOOP ELSE3 DECT R9 *Stack large subfile MOV @LSFR,*R9 *PUSH R DECT R9 MOV @LSFL,*R9 *PUSH L MOV @SSFL,R2 *Sort small subfile MOV @SSFR,R3 JMP MNLOOP * * Insertion Sort finishing up * INSERT MOV R1,R4 DECT R4 *I:=N-1 C R4,R0 JL LEAVE *Check if any looping at al FORI C @2(R4),*R4 JGT NEXTI *If next is greater than this, it's OK MOV *R4,R6 *KEY:=A[I] MOV R4,R5 INCT R5 *J:=I+1 WHILE MOV *R5,@-2(R5) *A[J-1]:=A[J] INCT R5 *J:=J+1 C R5,R1 JH ENDWHL *J>N? C *R5,R6 *A[J]<KEY? JLT WHILE ENDWHL MOV R6,@-2(R5) *A[J-1]:=KEY NEXTI DECT R4 C R4,R0 *Check if passed array base point JHE FORI LEAVE RTWP * Return Forth workspace * AI R6,4 * remove parameters from Forth stack B @R10 * Branch to Forth interpreter *-------------- * DATA AREA SORTWS BSS >20 *Workspace for sorting routine SUBSTK BSS >40 *Internal subfile stack ENDSTK EQU SUBSTK+>40 *End of that stack * variable used by quicksort LSFL DATA 0 *Large SubFile Left pointer LSFR DATA 0 *Large SubFile Right pointer SSFL DATA 0 *Small SubFile Left pointer SSFR DATA 0 *Small SubFile Right pointer END
-
3
-
-
DAM! Now I gotta use different VDP pages for GRAPHICS and TEXT mode.
Thanks for this excellent advert.
Camel99 Forth Information goes here
in TI-99/4A Development
Posted
I have never used it but I understand that the 6809 could do DTC NEXT in one instruction as well.
There is lot of stuff I don't understand but when I looked at the RISC 5 instruction set it seems extremely verbose to do simple things.
I get the RISC idea but the choices made for what the instructions should be seem odd to me.
And... nobody seems to care about sub-routine calling overhead in these new designs. I suppose because memory is so cheap. Just put code inline.
Chuck Moore built machines that did sub-routine calls in 1 cycle and return was just bit 15 that could be set on your instruction so it was free.