sometimes99er Posted July 28, 2011 Share Posted July 28, 2011 (edited) This demo places small bricks on a wall. Each character row on the screen can hold two rows of bricks, the first can have 32 bricks, the second is shifted slightly to the left and will only hold 31 bricks. That's 63 (32+31) bricks per row, and with 24 rows, we might end up with 1,512 (63*24) bricks. Edited April 4, 2018 by sometimes99er 1 Quote Link to comment Share on other sites More sharing options...
RXB Posted July 28, 2011 Share Posted July 28, 2011 This demo places small bricks on a wall. Each character row on the screen can hold two rows of bricks, the first can have 32 bricks, the second is shifted slightly to the left and will only hold 31 bricks. That's 63 (32+31) bricks per row, and with 24 rows, we might end up with 1,512 (63*24) bricks. 100 CALL CLEAR::CALL SCREEN(15)::CALL COLOR(1,7,1):$="00FE000FE0EF"::FOR A=1 TO 7 110 CALL CHAR(32+A,RPT$(SEG$(P$,(A AND 1)*2+1,2),3)&"00"&RPT$(SEG$(P$,(A AND 6)+5,2),3))::NEXT A 120 FOR A=0 TO 1::R=INT(RND*24)+1::C=INT(RND*(32-A))+1::CALL GCHAR(R,C,G)::CALL HCHAR(R,C,G OR (1+A)) 130 IF A THEN CALL GCHAR(R,C+1,G)::CALL HCHAR(R,C+1,G OR 4) 140 NEXT A::GOTO 120 Good work, very cool. Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted July 29, 2011 Author Share Posted July 29, 2011 (edited) Thanks. The same program, but probably just a bit more "readable": Characters 32 thru 39: Edited April 4, 2018 by sometimes99er Quote Link to comment Share on other sites More sharing options...
lucien2 Posted July 29, 2011 Share Posted July 29, 2011 Really nice. You are a coding artist. You removed the best part. CALL CHAR(32+A,RPT$(SEG$(P$,(A AND 1)*2+1,2),3)&"00"&RPT$(SEG$(P$,(A AND 6)+5,2),3)) 1 Quote Link to comment Share on other sites More sharing options...
Willsy Posted July 29, 2011 Share Posted July 29, 2011 First, for fun, I benchmarked the two XB programs: Program A: 100 CALL CLEAR::CALL SCREEN(15)::CALL COLOR(1,7,1):$="00FE000FE0EF"::FOR A=1 TO 7 110 CALL CHAR(32+A,RPT$(SEG$(P$,(A AND 1)*2+1,2),3)&"00"&RPT$(SEG$(P$,(A AND 6)+5,2),3))::NEXT A 120 FOR I=1 TO 1000::FOR A=0 TO 1::R=INT(RND*24)+1::C=INT(RND*(32-A))+1::CALL GCHAR(R,C,G)::CALL HCHAR(R,C,G OR (1+A)) 130 IF A THEN CALL GCHAR(R,C+1,G)::CALL HCHAR(R,C+1,G OR 4) 140 NEXT A::NEXT I Run time in XB (classic99, no overdrive etc): 7 Minutes 27 Seconds (447 Seconds) Program B: 100 CALL CLEAR::CALL SCREEN(15)::CALL COLOR(1,7,1) 110 CALL CHAR(33,"FEFEFE") 120 CALL CHAR(34,"000000000F0F0F") 130 CALL CHAR(35,"FEFEFE000F0F0F") 140 CALL CHAR(36,"00000000E0E0E") 150 CALL CHAR(37,"FEFEFE00E0E0E") 160 CALL CHAR(38,"00000000EFEFEF") 170 CALL CHAR(39,"FEFEFE00EFEFEF") REM TOP BRICK IN ROW 175 FOR I=1 TO 1000 180 R=INT(RND*24)+1::C=INT(RND*32)+1 190 CALL GCHAR(R,C,G)::CALL HCHAR(R,C,G OR 1) REM LOWER BRICK IN ROW 200 R=INT(RND*24)+1::C=INT(RND*31)+1 210 CALL GCHAR(R,C,G)::CALL HCHAR(R,C,G OR 2) 220 CALL GCHAR(R,C+1,G)::CALL HCHAR(R,C+1,G OR 4) 230 NEXT I Run time in XB (classic99, no overdrive etc): 6 Minutes 12 Seconds (372 Seconds) Then I knocked up the following (just paste into TF then type BRICKS): 0 VALUE SEED $8379 V@ TO SEED : UDG HEX DATA 4 FEFE FE00 0000 0000 DATA 4 0000 0000 0F0F 0F00 DATA 4 FEFE FE00 0F0F 0F00 DATA 4 0000 0000 E0E0 E000 DATA 4 FEFE FE00 E0E0 E000 DATA 4 0000 0000 EFEF EF00 DATA 4 FEFE FE00 EFEF EF00 DECIMAL 6 FOR I 33 + DCHAR NEXT ; : RND SEED 31421 * 6927 @ + DUP TO SEED ; : BRICKS 1 GMODE 14 SCREEN 4 6 0 COLOR UDG 1000 FOR \ top brick in row RND 23 MOD 5 << RND 31 MOD + DUP V@ 1 OR SWAP V! \ lower brick in row RND 23 MOD 5 << RND 31 MOD + DUP DUP V@ 2 OR SWAP V! 1+ DUP V@ 4 OR SWAP V! NEXT ; Run time in TF (classic99, no overdrive etc): 5 Seconds Making TF: * 89.4 times faster than program A (8840%) * 74.4 times faster than program B (7340%) Counting the instructions inside the loop in the Forth program, I count 38 instructions. Given that the loop is 1000 times, thats 38,000 instructions. 38000/5=7600 FIPS (Forth instructions per second). However, it's a bit faster than this, as I start timing (as best I can) when I hit ENTER to run the code, which includes: Initialise random number seed Set graphics mode Clear screen Set color set Define 7 characters It's very difficult to factor that out with the tools we have. I would approximate that TF, on this particular test, can do ~8000 FIPS. Clearly, assembly can beat this, and GCC too, since GCC churns out assembly. Mark 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted July 30, 2011 Author Share Posted July 30, 2011 Excellent. The more Forth code I see, the easier it looks. I think it might be creeping in under my skin. Is it that the FOR NEXT need DECIMAL ? I like the elegance of V@ and V! as a nice alternative to GCHAR and HCHAR, and the code even becomes more compact - perhaps not simpler, guess that depends on how you look at it. And the speed is a pure delight. CPU Overdrive (Classic99) is of course a bit of cheating when running XB, also with TurboForth, but it then becomes totally insane. I like that. Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 1, 2011 Share Posted August 1, 2011 Hi there! No, it's not the FOR code that needs DECIMAL - if you look above it, just after we create the word UDG (: UDG) we switch into HEX mode with the word HEX. This simply tells the compiler that any number that it 'sees' should be seen as a hex number. We then define the characters using good old hex, and then switch the compiler back to DECIMAL again. HEX is simply short for: 16 BASE ! DECIMAL: 10 BASE ! Say you wanted to view everything in binary... You would do 2 BASE ! - this puts the number 2 on the stack, then BASE executes - BASE simply puts an address on the stack - the address of the variable that holds the current number base. ! is the forth version of POKE. It requires a value (in this case 2) on the stack, and an address on the stack. Thus, the value 2 gets poked into the BASE register, and the base instantly changes. Bases from 2 to 36 are supported (!) Note: If you were in HEX mode, and you wanted to go back to decimal, you would have to do A BASE ! - because A is 10 in hex! This can get confusing real quick, so DECIMAL and HEX is provided to allow you to switch between the most popular two number bases from any base that you might be in at the time. Would you like an explanation of how the code bricks code works, or are you happy with it? Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted August 1, 2011 Author Share Posted August 1, 2011 Hello Englishman I'm very happy with it. And thanks for the notes, it cleared away my confusion there. I think I should be about ready to do my own TurboForth demo coding. I'll probably do an XB first to get any logical problems out of the way, knowing that it works, and then "translate" to TurboForth. Paper and pencil is always there ... PS. Why does my brain begin to feel like a haystack ? Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 1, 2011 Share Posted August 1, 2011 PS. Why does my brain begin to feel like a haystack ? Yep. Forth does that! It takes a while. I am still learning it too! Coding Forth is a real "brain workout!" - It's like fitness for the brain! It's kind of like Suduko, but very satisfying when you get it right... Then you want to re-visit the code and see if you can make it better! Why is Forth so different? Is it the stack. I actually don't think it's the stack. We often use the stack on Z80 and Intel processors. The thing with Forth, is that you are managing information flow (data, via the stack), *and* program flow. In most other languages, we only manage program flow. In forth, you have to imagine the stack in your head. It takes a while. And yes, pencil and paper is an excellent way to do it. And use vertical code techniques with stack comments, and re-write it horizontal later when you have it working. For example: : WIPE 0 ( TLA) 959 ( BRA) 480 0 DO DUP ( TLA BRA BRA) 42 ( TLA BRA BRA 42) SWAP ( TLA BRA 42 BRA) V! ( TLA BRA) 1- ( TLA BRA-1) SWAP ( BRA TLA) DUP ( BRA TLA TLA) 42 ( BRA TLA TLA 42) SWAP ( BRA TLA 42 TLA) V! ( BRA TLA) 1+ ( BRA TLA+1) SWAP ( TLA BRA) LOOP 2DROP ( --) ; TLA=top left address BRA=bottom right address Above I've shown the contents of the stack after every instruction. You can paste the code into TF and it will run. The 2DROP just drops the TLA and BRA from the stack (2DROP=drop topmost two items) to leave the stack clean on exit. In case you didn't know, TF tells you how many items there are on the stack while you are entering code. Type: 1 2 3 TF responds with ok:3 to tell you there are three items on the stack. Now type + and you get ok:2 What happened? Well, + added the top two stack items, replacing them with their sum. Want to see what's on the stack? .S TF replies: 1 5 < OK:2 The < sign is pointing to the top of the stack (top of stack on the right, bottom on the left). Sorry if you already know all this. It might help others though Later, when you have your code running, you can re-write in the traditional horizontal Forth style, maybe something like this: : WIPE 0 959 480 0 DO DUP 42 SWAP V! 1- SWAP DUP 42 SWAP V! 1+ SWAP LOOP 2DROP ; You could then paste that code into the block editor: 20 EDIT (select paste in Classic99) Press FCTN 9 to quit the editor. Type FLUSH to write the block back to disk. Now the code is on disk on block 20. You can load it anytime with 20 LOAD Before I go: To make a BLOCKS file to store code in: MKDSK DSK1.BLOCKS <size> E.g. MKDSK DSK1.BLOCKS 80 Creates an 80K (80 blocks/screens) file to hold code/data/anything you like. TF by default looks for a file called DSK1.BLOCKS on startup. If your blocks file is on another drive (e.g. DSK3) then hold 3 as TF starts. If you want to bypass auto-load then hold ENTER as TF starts. Final word re blocks files: Blocks files, when loaded with LOAD are basically batch files, and are extremely powerful. The easiest way to think of blocks files is to remember that LOAD basically "fools" TF into thinking that the stuff in the block has been typed in via the keyboard! Therefore you can have interactive loading, e.g., on block 20: .( Compiling WIPE) : WIPE 0 959 480 0 DO DUP 42 SWAP V! 1- SWAP DUP 42 SWAP V! 1+ SWAP LOOP 2DROP ; .( Compiled. Type WIPE to test) Hmmm... Ok, sorry, I didn't want to take over this thread - hope you don't mind - just wanted to save you pulling your hair out if you do try TF Mark Quote Link to comment Share on other sites More sharing options...
jacquesg Posted August 1, 2011 Share Posted August 1, 2011 Mark, I like your example. I have tried to do similar listings of stack contents and I often get confused when I get to a line such as 480 0 DO because I then put 480 and 0 on the stack listing and then some subsequent statements become difficult to understand. From your example it seems that 480 and 0 perhaps go on the stack but DO moves both items to somewhere else so that LOOP can use them to know when the looping statements are finished. Please let me know if my understanding is not correct. Jacques Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 1, 2011 Share Posted August 1, 2011 Jacques, Your understanding is 100% correct. DO actually moves them to the return stack and LOOP manipulates them there, throwing them away when the loop is finished. Mark 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.