-
Posts
4,470 -
Joined
-
Last visited
Content Type
Profiles
Forums
Blogs
Gallery
Events
Store
Posts posted by TheBF
-
-
MOVE in ANS Forth does a test to determine if it should do CMOVE or CMOVE>.
(not very true to Forth philosophy but there it is)
I just tried it before I posted to make sure that darn thing actually worked.
It seems to.
Maybe because my top of stack in R4. ?
(Way back , after I found the recursive one in your source code, I just played around until something seemed to work. I have not actually put it in a program to your point.)
- 1
-
Here is a word you can tuck away on a TURBO Forth block somewhere.
POSTPONE replaces COMPILE and [COMPILE] and so it can make translating ANS Forth code a bit easier.
I tried it on TF and it seems to work.
: POSTPONE ( <name> -- ) \ replaces COMPILE and [COMPILE] BL WORD FIND DUP 0= ABORT" POSTPONE can' find" 0< IF COMPILE COMPILE THEN , ; IMMEDIATE
You use POSTPONE like COMPILE or [COMPILE] but it knows the difference between compiling an IMMEDIATE word or a normal word.
Example: > is normal, IF is immediate. No problem
: >IF POSTPONE > POSTPONE IF ; IMMEDIATE
Test
: TEST >IF ." bigger" ELSE ." less than" THEN ;
- 1
-
Here is another way that I did ROLL.
: ROLL \ nn..n0 n -- nn-1..n0 nn ; 6.2.2150 DUP>R PICK SP@ DUP CELL+ R> 1+ CELLS MOVE DROP ;
-
52 minutes ago, Lee Stewart said:
OK...Here is an ALC recursion that works! It is 40 bytes, so 6 bytes longer than the simple loop, but it certainly is satisfying to accomplish it!
ROLL DATA $+2 LI R7,ROLLIT ;load entry point for recursive call MOV *SP+,R2 ;pop position (count) BL *R7 ROLLEX B *NEXT ROLLIT *++ Push return address to return stack DECT R MOV R11,*R *++ 1- DEC R2 *++ IF JLT ROLITX ;we're done if negative *++ >R DECT R MOV *SP+,*R *++ MYSELF (recurse) BL *R7 *++ SWAP R> (pop return stack to stack and SWAP) DECT SP MOV @2(SP),*SP ;over MOV *R+,@2(SP) ;under *++ Pop return address and return ROLITX MOV *R+,R6 B *R6
Can it be refactored to tighter code?
...lee
Is there a way to not load R7 and rather replace the MYSELF line with
BL @ROLLIT
or some such computed address to the entry to the code
-
30 minutes ago, apersson850 said:
I don't know which language they intended that to be, but it's not Pascal. In Pascal, it would be like one of these two:
I just peeked in the text and they mention they are using what they call "Super Pascal" to express the alogrithms.
So this is "psuedo-pascal" in reality.
-
46 minutes ago, Lee Stewart said:
OK...Here is an ALC recursion that works! It is 40 bytes, so 6 bytes longer than the simple loop, but it certainly is satisfying to accomplish it!
ROLL DATA $+2 LI R7,ROLLIT ;load entry point for recursive call MOV *SP+,R2 ;pop position (count) BL *R7 ROLLEX B *NEXT ROLLIT *++ Push return address to return stack DECT R MOV R11,*R *++ 1- DEC R2 *++ IF JLT ROLITX ;we're done if negative *++ >R DECT R MOV *SP+,*R *++ MYSELF (recurse) BL *R7 *++ SWAP R> (pop return stack to stack and SWAP) DECT SP MOV @2(SP),*SP ;over MOV *R+,@2(SP) ;under *++ Pop return address and return ROLITX MOV *R+,R6 B *R6
Can it be refactored to tighter code?
...lee
That is totally awesome. I got called away and didn't get to play with this.
Felicitations!
- 1
-
21 minutes ago, apersson850 said:
I don't know which language they intended that to be, but it's not Pascal. In Pascal, it would be like one of these two:
function empty(q:queue): boolean; begin if q.front=q.rear then empty := true else empty := false end; function empty(q:queue): boolean; begin empty := q.front=q.rear end;
Just because you don't use Forth you don't have to overcomplicate things.
Very nice. It shows how people who write text books don't always have a clear view on the real world engineering demands.
If you do complicate things now Herr Wirth will probably come to haunt you.
I notice that I typed that in wrong with that leading '('. mea culpa.
-
By the way I counted wrong in that comment above.
GForth takes 10 instructions in Intel land.
Code pick ( $40339C ) mov dword ptr 41F2D8 , ebp \ $89 $2D $D8 $F2 $41 $0 ( $4033A2 ) mov eax , dword ptr [edi] \ $8B $7 ( $4033A4 ) add edi , # 4 \ $83 $C7 $4 ( $4033A7 ) mov eax , dword ptr [edi] [eax*4] \ $8B $4 $87 ( $4033AA ) sub edi , # 4 \ $83 $EF $4 ( $4033AD ) mov dword ptr [edi] , eax \ $89 $7 ( $4033AF ) mov eax , dword ptr 0 [ebp] \ $8B $45 $0 ( $4033B2 ) add ebp , # 4 \ $83 $C5 $4 ( $4033B5 ) mov esi , eax \ $89 $C6 ( $4033B7 ) mov ebx , dword ptr [eax] \ $8B $18 ( $4033B9 ) jmp 40152A \ $E9 $6C $E1 $FF $FF end-code ok
But I just checked out VFX Forth's version!
see pick PICK ( 004323D0 488B5CDD00 ) MOV RBX, [+RBX*8] ( 004323D5 C3 ) RET/NEXT ( 6 bytes, 2 instructions ) ok
- 1
-
After I saw your Forth version I took the approach that since "thou shalt not roll", the recursive Forth version was good enough.
My Pick looks like this. I never considered putting ABS in there.
CODE PICK ( n -- n) \ GForth ITC takes 8 intel instructions for PICK TOS 1 SLA, \ (" n CELLS") SP TOS ADD, *TOS TOS MOV, NEXT, ENDCODE
But in mission critical uses I load 3RD and 4TH which are the same size as OVER in 9900 code.
HEX CODE 3RD ( a b c d -- a b c d b) \ ANS: 2 PICK 0646 , C584 , \ TOS PUSH, C126 , 0004 , \ 4 (SP) TOS MOV, NEXT, ENDCODE CODE 4TH ( a b c d e-- a b c d e a) \ ANS: 3 PICK 0646 , C584 , \ TOS PUSH, C126 , 0006 , \ 6 (SP) TOS MOV, NEXT, ENDCODE
- 1
-
2 hours ago, khanivore said:
Looking at the generated code, while gcc normally calls a function like this:
bl @puts
If you optimise using -O2 or -Os it decides to use an intermediate register for some reason:li r2, puts bl *r2
which is generally worse not better. It might be slightly better if the load was outside a loop but the compiler rarely does the load outside the loop from what I've seen.
Even worse, when it runs short of registers it spills the reg to the stack resulting in this absolute mess:li r2, testColorText mov r2, @>4(r10) ... mov @>4(r10), r2 bl *r2
I've been desperately trying to find a way to stop it from storing labels to registers before doing a branch or call and I just can't. I added cost functions that made calls to registers prohibitively expensive but they made no difference. I tried a peephole which sometimes worked but it crashed when I encountered cases like above where the label had been pushed to the stack.
Then I stumbled across the switch -fno-function-cse which happily stops gcc from doing the stupidity above. Even with -Os now we don't get label stores to regs. I'd recommend everyone add this to their CFLAGS in their Makefiles if using optimisation. So I'm going to consider this issue as "closed won't fix" as there is a valid workaround 🙂That's an amazing win. Well done.
I am absolutely flabbergasted at the complexity of this monster, and the patience you guys have to keeping flipping switches until it does your bidding.
It flies in the exact opposite direction of what I play with... but it gets results.
I joked about writing the lisp dialect that controls this GCC process in RPN but after pondering it for while I realize it is not a crazy idea.
Forth like LISP makes creating a domain specific language pretty simple.
in fact boolean functions in RPN are quite neat and have far less brackets.
Example so you know what I mean and then I will stop distracting the thread.
HEX DEAD 00FF AND 0080 OR -1 XOR . \ dot prints the result \ logic language extensions : NAND ( n n --?) AND NOT ; : NOR ( n n --?) OR NOT ;
Your head stops hurting after a short time using it and testing stuff in the REPL.
>>We now rejoin our regularly scheduled program already in progress<<
- 1
- 3
-
I think recursion here is a "call" in machine code. So for 9900 we would need to make the code a nestable sub-routine that pushes R11 on entry and pops R11 on exit.
Essentially you are making one Forth word a "subroutine threaded" word.
I will play around a bit too and see if I am blowing smoke on this.
Come to think of it I don't think I got around to making RECURSE in my machine code project so maybe I can try implementing and testing on that platform.
I predict a few crashes...
- 1
-
What would it look like if you strung the Forth primitives end-to-end, without NEXT, use Assembler IF ENDIF and a JMP to do the recursion?
: ROLL ( [n]..[0] +n --- [n-1]..[0][n] ) \ #instructions -DUP \ 3 IF \ 1 1- \ 1 SWAP \ 3 >R \ 2 MYSELF \ 1 ?? R> \ 2 SWAP \ 3 THEN ; \ = 15
Potentially the same number of instructions.
Maybe some optimizations could be found with SWAP because you are using registers in the code. So maybe you just SWAP the argument order?
Might worth a look.
- 1
-
Back 2021 I needed a way to capture text output from programs.
I threw something together but it was not ideal. For example if you sent strings to the screen without a CR now and then it would fail.
This updated version is more robust and bettered factored.
>> If you have a Camel99 system disk, please replace DSK1.OUTFILE with this file.
Of note to the student of Forth on TI-99, I am accessing the PAB, in VDP memory, just as easily as CPU RAM by using the VDP operators V@ V! VC@ and VC!.
The do the same things as their regular Forth namesakes.
The various fields of PAB are accessed referenced to the "SELECTed" file PAB with a syntax using the active PAB with a field selector word.
The field selector simply adds an offset to the base address of the PAB. These are the words that have square brackets.
To use this file you INCLUDE DSK1.OUTFILE first. Then any program loaded afterwords will use the dual output versions of the text output words.
If you MAKE-OUTPUT or OPEN-OUTPUT (append mode) the program text will go to the screen and echo to the file.
Kernel words that use the kernel output words will not echo to the file.
In future I might patch the kernel words which would make everything to echo but at this time I don't need that.
Spoiler\ OUTFILE.FTH echo screen output to text file May 2021 Brian Fox \ updated Jan 2024 \ Method: Write data directly into PAB file buffer \ Use the PAB char count in the PAB as pointer into the PAB when we write. \ ie: the buffer address= [PAB FBUFF] V@ + [PAB CHARS] VC@ \ Only write to disk when CR is encountered or if buffer will overflow. \ No control characters allowed. Use spaces for DV80 files \ *** THIS VERSION WORKS **** NEEDS WRITE-FILE FROM DSK1.ANSFILES NEEDS VALUE FROM DSK1.VALUES DECIMAL 0 VALUE OUTH \ output file handle VARIABLE FOUT \ byte counter for outfile : MAKE-OUTPUT ( a u -- ) \ *G creates a new output file DV80 W/O CREATE-FILE ?FILERR TO OUTH FOUT OFF ; \ : W/A APPEND FAM @ ; \ Moved to DSK1.ANSFILES : .OUT ( -- ) FOUT @ U. ." bytes output" ; : OPEN-OUTPUT ( a u -- ) \ open output file in APPEND mode OUTH ABORT" Output file is already open" DV80 W/A OPEN-FILE ?FILERR TO OUTH FOUT OFF ; : CLOSE-OUTPUT ( -- ) OUTH CLOSE-FILE DROP 0 TO OUTH .OUT ; : WRITE-PAB ( handle -- ) SELECT 3 FILEOP ?FILERR ; : [PABCHARS]+! ( n -- ) [PAB CHARS] VC@ + [PAB CHARS] VC! ; : [OUTBUFF] ( -- Vaddr) [PAB FBUFF] V@ [PAB CHARS] VC@ + ; : FLUSH-BUFFER ( -- ) OUTH WRITE-PAB 0 [PAB CHARS] VC! ; : OVERFLOW? ( n -- ?) \ test n bytes will overflow buffer [PAB CHARS] VC@ + \ add n to chars in the buffer [PAB RECLEN] VC@ > \ compare to the maximum size ; : >>OUT ( caddr len -- ) OUTH 0= ABORT" Output file not open" OUTH SELECT DUP OVERFLOW? IF FLUSH-BUFFER THEN TUCK ( -- len caddr len ) \ get a copy of the length [OUTBUFF] SWAP VWRITE \ write string to PAB buffer ( len) DUP [PABCHARS]+! FOUT +! ; \ update Char count \ ========================================== \ redefine standard output words to echo to file if output handle<>0 : EMIT ( c --) OUTH IF DUP HERE C! HERE 1 >>OUT THEN EMIT ; : TYPE ( a u --) OUTH IF 2DUP >>OUT THEN TYPE ; : ." ( ccc" -- ) POSTPONE S" STATE @ IF POSTPONE TYPE EXIT THEN TYPE ; IMMEDIATE : SPACE BL EMIT ; : SPACES ( n -- ) 0 MAX 0 ?DO SPACE LOOP ; : CR ( -- ) OUTH IF \ file is open [PAB CHARS] VC@ 0= IF SPACE THEN FLUSH-BUFFER THEN CR ; \ number output with echo : UD. ( d -- ) <# #S #> TYPE SPACE ; : U. ( u -- ) 0 UD. ; : . ( n -- ) DUP ABS 0 <# #S ROT SIGN #> TYPE SPACE ;
- 1
-
I know you are well beyond the Intel hex thing now @VORTICON but I am trying to confirm/deny that code I got from MaxForth is generating a compliant checksum.
Based on a post by @apersson850 I think one line of your code would need to change to give the same result that my version generates.
On 1/19/2024 at 10:00 AM, apersson850 said:That was supposed to be the 1's complement. For 2's complement i used the odd(-n) instruction. A minus sign in Pascal is like NEG in assembly and that's the same as 2's complement.
Could someone confirm that the 2nd line below would be the correction from 1s' complement to 2's complement.
(Lee is watching I hope I spelled that right or I will only get part marks)
( ORIGINAL LINE } DATACHECK := ORD(NOT(ODD(DATACHECK)) AND ODD(255)) + 1; { Should be ? } DATACHECK := ORD((ODD(-DATACHECK)) AND ODD(255));
- 1
-
Here is a version of of LOADHEX that calculates a checksum as the file records are parsed and compares it to the checksum at the end of each record.
The checksum addition is embedded in each parsing word that cuts the record string and converts to a number.
If you type CHECKSUM OFF the loader will ignore the checksum byte at the end of each record.
CHECKSUM ON will cause the program to halt with an error and a line no. if the checksums do not match.
The MaxForth encoder seems to do something tricky. I have not figured out if it is compliant with the checksum specification
Randy's code adds each binary byte to a variable.
But the value it puts at the end of each record is that total, taking only the lower order bits , NEGATE that byte and mask with >FF.
(see CHKSUM@ below)
Anyway this loader uses what it was given. I will review the spec. and see if there is something wrong.
(I suspect Randy was smarter that I am in this area)
Spoiler\ HEXLOADER.FTH loads intel hex files into memory Fox 2024 INCLUDE DSK1.TOOLS INCLUDE DSK1.ANSFILES INCLUDE DSK1.VALUES VARIABLE CHKSUM \ Accumulator for to compute check sum VARIABLE CHECKSUM \ flag:. halts on checksum error if CHECKSUM=TRUE HEX \ renamed for clarity of purpose : LEFT$ ( addr len n -- addr len' ) NIP ; : RIGHT$ ( addr len m -- addr' len') /STRING ; : CHOP ( addr len n -- addr1 len1 addr2 len2 ) >R \ we need n twice, use return stack 2DUP R@ LEFT$ \ substring 2SWAP R> RIGHT$ \ "RIGHT$" is remainder 2SWAP ; \ substring back on top ; : $VAL ( addr len -- n) NUMBER? ABORT" Bad number" ; \ *** data management tags *** \ Words that are in angle brackets operate on a string. \ They take a part of the string convert it to a number \ and leave the remainder of the string on the data stack \ for the next function to use. \ aborts if start code not found : <?START> ( addr len -- addr' len') 1 CHOP DROP C@ [CHAR] : <> ABORT" Start code not found" ; \ field parsers update the chksum variable : <RECLEN> ( addr len -- C ) 2 CHOP $VAL DUP CHKSUM +! ; : <ADDRESS> ( addr len -- n ) 4 CHOP $VAL DUP SPLIT + CHKSUM +! ; : <RECTYPE> ( addr len -- C ) 2 CHOP $VAL DUP CHKSUM +! ; : <DATABYTE> ( addr len -- C ) 2 CHOP $VAL DUP CHKSUM +! ; \ This is the last tag. It consumes the string. Returns file's chksum : </CHKSUM> ( addr len -- n ) $VAL ; 0 VALUE #1 \ file handle holder : OPEN ( fam -- ) OPEN-FILE ?FILERR ; : READLN ( addr -- addr size ?) DUP 80 #1 READ-LINE NIP 0= ; : <DATALINE> ( addr len -- ) BOUNDS DO <DATABYTE> I C! LOOP ; \ This matches the checksum calc used by MaxForth's intel HEX save. : CHKSUM@ ( -- c) CHKSUM 1+ C@ NEGATE 0FF AND ; : .DEC BASE @ >R DECIMAL . R> BASE ! ; : ?ABORT CHECKSUM @ ABORT" LOADHEX halted" ; : ?CHECKSUM ( n -- ) CHKSUM@ <> IF CR ." >>> Checksum error Line: " LINES @ .DEC ?ABORT THEN ; : LOADHEX ( path$ len -- ) LINES OFF HEX DV80 R/O OPEN TO #1 BEGIN PAD READLN ( -- addr len ?) LINES 1+! WHILE ( read=true) CHKSUM OFF <?START> \ TEST for start code. Abort if wrong <RECLEN> >R \ use this later <ADDRESS> >R \ use this later ( r: -- len addr) <RECTYPE> 0= \ 0 means a data record WHILE R> R> ( -- addr len ) <DATALINE> \ parse the data and write to memory </CHKSUM> \ clean up what's left. Return file line chksum ?CHECKSUM \ compare to computed chksum from file load REPEAT UNLOOP \ first time I ever used this word :-) THEN 2DROP #1 CLOSE-FILE ?FILERR ; \ HEX 2000 2000 FF FILL \ for testing CHECKSUM ON
- 2
-
Some SAMS ideas that others may find useful. ?
In my SAMS system I remember the last SAMS page that was used in a memory location.
When it comes time to pull in a page I test if it's already in memory and jump around the mapping code.
Otherwise I update the variable, which adds one instruction to the mapping code.
This works well on simple setups where I just have one 4K window reserved for SAMS pages.
Another thing I have done is to think of 1M SAMS as 16 64K segments.
I keep a variable for the active segment.
This lets you use 16 bit address math and 64K is usually enough for a specific purpose in my programs.
Also have a piece of code that given a 16 bit address, it computes the SAMS page and the offset which is added to the RAM window address.
This can be done with a DIV by 4096, to get a quotient and remainder or with some bit twiddling that @Lee Stewart created for me.
This gives you a contiguous 64K virtual memory space that you can access sequentially like it really existed.
I would show you the code but it is in Forth's reverse polish Assembler so you need to read it hanging from the ceiling by your feet.
(so said @apersson850 one time)
And these ideas are not complicated for people to implement their own way.
- 2
-
-
3 hours ago, dhe said:
What is my best source for easily understood assembly code for manipulating the bank switching in a SAMS memory card?
For what it is worth I spent some time getting my head around the card from that same document.
Here is synopsis what have I learned at a high level. It might give you a jumpstart.
1. The SAMS card is controlled by the CRU I/O buss that the 9900 uses.
The base address for control of the card is >1E00
Turn on the card with bit '0' while R12 is holding >1E00
2. Once the card is on, the memory addresses starting at >4000 are a series of "registers" that control where each 4K "page" of SAMS is mapped into CPU RAM.
Register 0 at >4000 holds the value when you map SAMS into a CPU memory window at >0000
1 at >4002 for mapping SAMS into RAM >1000
2 at >4004 for mapping SAMS into RAM >2000
3 at >4006 for mapping SAMS into RAM >3000
etc.
If you only need one window for your software you only need to deal with one of these registers.
If you want 8K (2 pages) mapped you use two registers and so on...
3. What goes into the registers can be thought of like a number from 0 to >FF (for 1Meg cards) were each value is the 4K page that you want to map into the CPU RAM window.
(4K x >FF= 1M)
You must remember that this value goes in the left side of the 16bit number.
So if you want page 9 mapped into memory at >3000 in CPU RAM , you would do:
>0900 -> >4006
4. One thing that was not obvious to me was that the default values in the SAMS card setup the SAMS card to be the 1st 64K of RAM for the TI-99.
This is done by a "SAMSINI" program that simply fills the registers with sequential page numbers.
If you remap the memory at >A000,>B000,>C000, and above and you have a program in there it will crash.
5. Here is a way to map a SAMS page into CPU RAM >3000, in the smallest amount of code I know of.
(translated this without testing but I think it's correct)
* setup LI R0,9 \ put the sams page we want in R0 LI R12,>1E00 \ set SAMS card CRU address * map the page SWPB R0 \ get the page no. on the correct side 0SBO \ enable SAMS registers MOV R0,@4006 \ put the page number in the register for window >3000 in CPU RAM 0SBZ \ card off
Have fun!
- 2
-
Well..
The English opposite of Sanction, a threatened penalty for disobeying a law or rule is:
Sanction, official permission or approval for an action.
😕
- 1
-
Never thought of interrupts in the context of a microcode program.
Good insight.
Thanks.
- 1
-
7 hours ago, apersson850 said:
The p-system does some of this improvements by itself, since it pre-scans for relevant interrupts and stores their entry points at boot time.
When running, it polls the interrupt inputs and then execute the relevant code when it sees them. Still this is integrated with happening at suitable times in the PME's interpretation of p-code, so it will not react as fast as if you have a hardware interrupt allowed to happen between any machine instruction, of course.
I have two cards I designed myself in the expansion box. They both have interrupt capability, so I've tested adding an unknown (to the p-system designers) hardware interrupt, and that can work too.
In your substantial computer experience Is there any other machine besides TI-99 where the term "polls the interrupts" is used. It's like an oxymoron to me.
In my little world interrupts are about removing the need to poll.
-
11 minutes ago, Vorticon said:
I did read about UNITREAD/UNITWRITE but did not see anything about any "control word"...
In any case I did manage to get text files transferred using UNITREAD.
Oddly enough however, I noticed that there were 10 bytes of junk added to the end of the file even though the file itself transferred perfectly. Easy enough to edit out but not sure why this is happening. On the PC side I'm using the "Send File" option of TeraTerm which sends a raw file without any communication protocol. I do suspect however that I will have issues with larger files once the RS232 buffer is full since there is no flow control here.
Things did not go well for binary transfers however. No matter what I tried, I always got garbage bytes from beginning to end. Here's the binary transfer program:
All in all, I think I've already spent an inordinate amount of time experimenting with this, and my conclusion is that raw serial file transfers without protocols is a very dicey and unreliable affair.
I have no doubt your conclusion is correct.
With such a slow machine flow control is critical on the receive side in my experience without resorting to some new tricks for RS232 interrupt handling that were invented by someone who's name escapes me just now. I think I got a copy from @InsaneMultitasker
-
While looking for something else I found this question by @FarmerPotato.
"Does anyone know of a FORTH implementation of the TI tagged object file loader?
I found some old assembly code for a game. It would be nice if there were a way to use the assembled code so far in FORTH wrappers.
And continue to develop in FORTH. Changes to the assembly code would be assembled outside, then imported to the project. Ultimately the whole thing gets BSAVEd and run out of a FORTH prompt."
Well my latest linker seems more practical and I can save a binary program that also loads LOW ram with the set of files so I think I could get this going.
Do you still have those files Erik?
- 1
-
27 minutes ago, mizapf said:
The funny thing is that the phlogiston theory (17th-18th century) just postulated something similar for fire, before people learned that fire is an exothermic reaction with oxygen.
They believed that combustible substances were able to absorb phlogiston, and when burning, the phlogiston emerged into the air by the well-known appearance. When you put it in a closed container, the substance stopped burning because the air became saturated with phlogiston.
This is because of those three little words that men find so difficult to say:
"I don't know"
Red Green, Canadian comedian
- 3
- 1
fbForth—TI Forth with File-based Block I/O [Post #1 UPDATED: 06/09/2023]
in TI-99/4A Development
Posted
It's always been the thrill of the chase though hasn't it? I think your recursive ALC version was something of a first around here.
You could always make the fast versions a block of machine code for those sinful creatures that need to roll as fast as possible.