RXB Posted October 14, 2011 Share Posted October 14, 2011 I think a useful tool for Forth would by my RXB routines. CALL HGET(row,column, length,string-variable,[,...]) or CALL VGET(row,column,length,string-variable[,...]) also CALL HPUT(row,column,string) or CALL VPUT(row,column,string) or best of all CALL MOVES("from-to",length,from-address,to-address) or CALL MOVES("from-to",length,from-address,to-VDPregisters) "from-to" would be RV or VV or RV or GV or V# as R=RAM, V=VDP,G=GROM/GRAM, #=VDP Register I have strings in RXB and that one is $=strings so do not know it that would really apply to Forth. My RXB commands CALL ONKEY(string-list,key-unit,return-variable,status-variable)GOTO line-number-list might be good for Forth as it is a CALL KEY ON GOTO and IF value THEN in one command. Just a suggestion of ideas. Rich Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 14, 2011 Share Posted October 14, 2011 Hi Rich, Some nice ideas. Here's how I would tackle them in TurboForth: (I have no idea why the columns don't line up properly) : HGET ( row column length buffer -- ) >R >R \ buffer address & length to return stack SWAP XMAX * + \ calculate screen address R> \ get length from return stack R> \ get buffer address from return stack SWAP \ stack is now: saddr buffer length VMBR \ read from screen into buffer ; : HPUT ( row column length buffer -- ) >R >R \ buffer address & length to return stack SWAP XMAX * + \ calculate screen address R> \ get length from return stack R> \ get buffer address from return stack SWAP \ stack is now: saddr buffer length VMBW \ write to screen from buffer ; The above just use the built in TurboForth words VMBR and VMBW (which work the same was as the editor assembler equivalents). They are very fast because they don't have to do any limit checks, since they are working on consequtive VDP addresses. Here's the vertical equivalents: 0 VALUE EOS : VGET ( row column length buffer -- ) XMAX 24 * TO EOS \ compute end of screen address >R >R \ move buffer and length out of the way SWAP XMAX * + \ convert row and column to screen address R> \ get length back R> \ get buffer address back SWAP 0 DO \ repeat length times SWAP \ get screen address to top of stack DUP V@ \ read a byte from VDP 2 PICK \ get buffer address C! \ store byte in buffer XMAX + \ move down 1 row DUP \ copy new screen address EOS >= IF \ moved off bottom of screen? EOS - 1+ \ move to top of next column THEN SWAP \ get buffer address 1+ \ increase buffer address LOOP \ repeat 2DROP \ drop buffer address and screen address ; : VPUT ( row column length buffer -- ) XMAX 24 * TO EOS \ compute end of screen address >R >R \ move buffer and length out of the way SWAP XMAX * + \ convert row and column to screen address R> \ get length back R> \ get buffer address back SWAP 0 DO \ repeat length times DUP C@ \ get a byte from the buffer 2 PICK \ get screen address V! \ write the byte from the buffer to screen 1+ \ move to next buffer address SWAP \ get screen address on top of stack XMAX + \ move down 1 row DUP \ copy new screen address EOS >= IF \ moved off bottom of screen? EOS - 1+ \ move to top of next column THEN SWAP LOOP 2DROP ; Much more complex, as you can see. Because you are moving 'downwards' in VDP memory, you need to do a check to see if you have 'fallen off' the end of the screen, and correct the screen address if you have. Regarding saving and restoring entire areas of the screen, I decided to leverage the above HGET and HPUT code. The code below will save rectangular areas of the screen to a buffer in memory, and allow you to restore it later. You can restore just a portion (relative to the top left of the saved rectangle) if you like. Neat for a windowing system in text mode. 0 VALUE buffer 0 VALUE repeat 0 VALUE length 0 VALUE column 0 VALUE row : RGET ( row column length repeat buffer -- ) TO buffer TO repeat TO length TO column TO row repeat 0 DO row column length buffer HGET 1 +TO row length +TO buffer LOOP ; : RPUT ( row column length repeat buffer -- ) TO buffer TO repeat -ROT TO column TO row repeat 0 DO row column 2 PICK buffer HPUT 1 +TO row length +TO buffer LOOP DROP ; To test, put something on the screen. Use the word WORDS as it writes lots of stuff to the screen. Then, save the entire screen to memory: 0 0 40 24 HERE RGET (HERE is a word that returns the next free address in memory, so we'll be saving our screen data in CPU ram in un-used RAM - it means we don't have to explicitly ALLOT memory to hold our screen data). Now, clear the screen with PAGE. Now, restore a portion of the saved screen, but at a different location: 5 11 12 10 HERE RPUT Finally, here's the code again, but written in the classic horizontal Forth style, as you might see it in a disk block. : HGET ( row column length buffer -- ) >R >R SWAP XMAX * + R> R> SWAP VMBR ; : HPUT ( row column length buffer -- ) >R >R SWAP XMAX * + R> R> SWAP VMBW ; 0 VALUE EOS : VGET ( row column length buffer -- ) XMAX 24 * TO EOS >R >R SWAP XMAX * + R> R> SWAP 0 DO SWAP DUP V@ 2 PICK C! XMAX + DUP EOS >= IF EOS - 1+ THEN SWAP 1+ LOOP 2DROP ; : VPUT ( row column length buffer -- ) XMAX 24 * TO EOS >R >R SWAP XMAX * + R> R> SWAP 0 DO DUP C@ 2 PICK V! 1+ SWAP XMAX + DUP EOS >= IF EOS - 1+ THEN SWAP LOOP 2DROP ; 0 VALUE buffer 0 VALUE repeat 0 VALUE length 0 VALUE column 0 VALUE row : RGET ( row column length repeat buffer -- ) TO buffer TO repeat TO length TO column TO row repeat 0 DO row column length buffer HGET 1 +TO row length +TO buffer LOOP ; : RPUT ( row column length repeat buffer -- ) TO buffer TO repeat -ROT TO column TO row repeat 0 DO row column 2 PICK buffer HPUT 1 +TO row length +TO buffer LOOP DROP ; Note that all of the above code uses the word XMAX which returns the number of columns on the screen (32, 40 or 80). Thus the above code works in 32, 40 or 80 column modes with no changes at all. I'll tackle the ONKEY example when I get a chance, hopefully over the weekend. Mark Quote Link to comment Share on other sites More sharing options...
RXB Posted October 14, 2011 Share Posted October 14, 2011 WOW very cool Willsy! That was quick. I had a command years ago in RXB called JOKE(joy-y1,joy-x1,joy-y2,joy-x2,key-return1,key-return2,status-return) scans Joystick 1 and 2 and key unit 1 and 2 in one command. JOKE sounded cooler then JOYKEY, well it just sounded gay. When I wrote version 1005 RXB I had one called JOKES(joy-y1,joy-x1,key-return1,key-return2,status-return,sprite#,sprite#) this would control from both joysticks 2 sprites in one command. But you could also use CALL JOKES(joy-y1,joy-x1,key-return1,status-return,sprite#) would be great for games but XB was just to flipn slow to make much use of it. Anyway do you think my CALL MOVES would be best for Forth as you might put a flag in it for character strings vs say pixels? (This would be impossible in XB) Quote Link to comment Share on other sites More sharing options...
Opry99er Posted October 18, 2011 Share Posted October 18, 2011 Hey Willsy... been trying to get some results in tf and making some progress. A few questions for you, if you don't mind... 1) Could you put a couple examples of simple sprite setting and moving? I tried to create a SPRITE and had an issue. Here's the word and I tried to attach a pic. : ADSP PAGE 0 MAGNIFY 1 10 10 42 15 SPRITE BEGIN BREAK? AGAIN ; This would create a SPRITE #1 at location 10,10, ascii value of 42 (*) and white... Not exactly showing up as I'd have thought... 2) Here's an issue I'm having with getting a couple of words to execute... This is in BLOCK 6... I could combine this and make it smaller, but I wanted to make it as easy to read as possible. Here I create two variables, store them with values, fetch them from memory, and attempt to use them. : MAKVAR VARIABLE PX VARIABLE PY ; : SETVAR 10 PY ! 5 PX ! ; : PCSET PY @ PX @ 42 1 HCHAR ; : TEST MAKVAR SETVAR PCSET ; When I do this, I FLUSH, then COLD, then 6 LOAD... it then gives me the following error message: Error:PY not found in SETVAR in block 6 Attempting to execute any of the words results in "Error:TEST not found" I tried re-organizing it... : MAKVAR VARIABLE PX VARIABLE PY ; : SETVAR 10 PY ! 5 PX ! ; : PCSET PY @ PX @ 42 1 HCHAR ; : TEST MAKVAR SETVAR PCSET ; Also I tried combining MAKVAR and SETVAR: : SETVAR VARIABLE PX VARIABLE PY 10 PY ! 5 PX ! ; and then changing TEST to: : TEST SETVAR PCSET ; Same result. When I try to do MAKVAR and SETVAR in immediate mode, it SEEMS to work... I attached 3 pictures... The test in IMMEDIATE mode and 2 pictures of the EDITOR mode test. Also tried using GOTOXY in both IMMEDIATE and EDITOR modes... In immediate mode, it worked perfectly... VARIABLE PY VARIABLE PX 5 PY ! 10 PX ! PY @ PX @ GOTOXY 42 EMIT The numbers were a bit different, but the same result... worked in IMMEDIATE, not inside a colon definition. I noticed that figFORTH (specifically TI Forth) utilizes a number on the stack when initializing a variable... (n -- ) but there's nothing about that in your glossary. Since there was nothing about that in Starting FORTH by Brodie, I assumed TF got around that. Then my next attempt... using stack only... no variables. In this case, my stack contains a y and an x value... 10 and 5. When "KEY" is called, the ascii code is placed on top of the stack. : SETVAR 10 5 ; : TEST SETVAR BEGIN BREAK? 2DUP PAGE 42 1 HCHAR (duplicate top two numbers, clear screen, place "*" onscreen) KEY 68 - 0 = IF DROP 1 - ELSE (if "E" then drop ascii and subtract 1 from top stack item) 1 = IF DROP SWAP 1 + SWAP ELSE (if "D" then add 1 to second stack item) 15 = IF DROP SWAP 1 - SWAP ELSE (if "S" then subtract 1 from second stack item) 20 = IF DROP 1 + ELSE (if "X" then add 1 to top stack item) DROP (if the key is none of these, drop it from the stack and complete loop) THEN THEN THEN THEN AGAIN ; I am getting some crazy results... starts off okay, but then the screen starts changing color and a mess follows pretty quickly. I haven't really spent much time trying to modify this... I just started messing with this concept earlier this morning... keeping both values on the stack at all times. Perhaps I'm missing some very simple stuff, so I'm posing it here to learn. =) Thanks in advance for your answers... I'm sure I'm missing simple stuff... but I'm new to Forth and I'm here to learn from the master. =) BTW, I spent some time in AceForth last night... Love to have an Ace someday. =) Owen Brand Quote Link to comment Share on other sites More sharing options...
Opry99er Posted October 18, 2011 Share Posted October 18, 2011 sorry, it messed up my formatting... I didn't put code brackets in there... I hope you get the idea... the last one was: : SETVAR 10 5 ; : TEST SETVAR BEGIN BREAK? 2DUP PAGE 42 1 HCHAR (duplicate top two numbers, clear screen, place "*" onscreen) KEY 68 - 0 = IF DROP 1 - ELSE (if "E" then drop ascii and subtract 1 from top stack item) 1 = IF DROP SWAP 1 + SWAP ELSE (if "D" then add 1 to second stack item) 15 = IF DROP SWAP 1 - SWAP ELSE (if "S" then subtract 1 from second stack item) 20 = IF DROP 1 + ELSE (if "X" then add 1 to top stack item) DROP (if the key is none of these, drop it from the stack and complete loop) THEN THEN THEN THEN AGAIN ; Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 18, 2011 Share Posted October 18, 2011 Okay, the sprite problem first. Let's have a look at your code: : ADSP PAGE 0 MAGNIFY 1 10 10 42 15 SPRITE BEGIN BREAK? AGAIN ; Right. A few problems with this. Firstly, you have not selected 32 column mode - you're still in 40 column mode, so sprites won't work at all! The 'noise' you see on the screen in 40 column mode is the SPRITE routine writing into the Sprite Attribute List (SAL) - which is just normal visible screen area in 40 column mode. So, you need a 1 GMODE at the beginning (which also clears the screen) to put it into 32 column mode. Second problem: You are using sprite 1, but sprites begin with 0 in TF. Why is this a problem? Well, you might not be aware but the VDP chip is designed to disable a sprite *and all sprites after it* if its Y value is >D0 (208 decimal). TF, when it selects 32 column mode, disables ALL sprites by writing 208 to the Y value of sprite 0 (actually, it writes it to all 32 sprites). So, by selecting sprite #1, you'll never see it, because sprite 0 has a Y coordinate of 208, which has disabled ALL the sprites. Therefore, you need to use sprite #0 first, #1 next, #2 next etc (or, just set their coordinates to 0 0 and give them a transparent colour (thinking about it, I probably should have done it that way in the first place... Arse...) Lastly (!) you STILL won't see anything (!), because sprites in TF use their own character set - they do not share characters with the the normal displayable 'tile' characters. You have 256 8x8 characters that can be used for sprites, completely seperately from the normal ASCII characters. So, you won't see anything, because the sprite graphic definitions are all zeros! You need to put some data there! You can use DCHAR for this just as you do with normal ASCII characters; just add 256 to the ASCII code to define the equivalent sprite character. So, let's put all of the above into code: : TEST 1 GMODE \ select 32 column mode \ define a cross hatch character for *sprite* char 42: DATA 4 $AA55 $AA55 $AA55 $AA55 42 256 + DCHAR \ now use sprite 0 0 10 10 42 15 SPRITE ; So, sprite character 42 is now a cross-hatch shape. It's important to get away from thinking that you have re-defined the asterisk - you haven't. The asterisk is ASCII character 42. We have defined sprite character 42. The asterisk is still there - try it with 42 EMIT It's occurred to me as a result of writing this reply that the sprites being physically disabled might confuse the hell out of people - people would naturally expect to be able to use any sprite in any order. As a result, I have just changed the code that initialises the sprites in 32 column mode. It now just sets their colour to transparent, which obviously makes them invisible (so you don't get random sprites appearing instantly when you select 32 column mode), but it means you can use any sprite (e.g. sprite 1 as in your example and it will work). This change will be in version 1.1 Hope this helps! Mark Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 18, 2011 Share Posted October 18, 2011 Okay, your second issue, using variables. Here's your code: : MAKVAR VARIABLE PX VARIABLE PY ; : SETVAR 10 PY ! 5 PX ! ; : PCSET PY @ PX @ 42 1 HCHAR ; : TEST MAKVAR SETVAR PCSET ; The problem is that the declaration of variables does not go inside a colon definition. They can only go outside of a colon definition, by themselves: VARIABLE PX VARIABLE PY : SETVAR 10 PY ! 5 PX ! ; : PCSET PY @ PX @ 42 1 HCHAR ; : TEST SETVAR PCSET ; Now, it works! SETVAR can be made more flexible by removing the constants, and simply taking data from the stack. So, if you change SETVAR to : SETVAR ( row column -- ) PX ! PY ! ; Note the stack comment which tells the reader what SETVAR expects on the stack before it is called, and shows that these two values are 'consumed' after the word is finished. Now, we can enter rows and columns on the command line, and call TEST, like this: Et voila! Note: TF also has VALUES - these work like variables, but: They take an initial value when created They return their value when referenced, not their address They are written to with TO not ! They are read by just giving their name Example: 99 VALUE TI TI . 99 ok:0 100 TO TI TI . 100 ok:0 25 +TO TI TI . 125 ok:0 I personally find them nicer to use, because you don't get the 'noise' of @ and ! in your source code: 0 VALUE PX 0 VALUE PY : SETVAL 10 TO PY 5 TO PX ; : PCSET PY PX 42 1 HCHAR ; : TEST SETVAL PCSET ; Note also that VALUEs are initialised outside of colon definitions. They also need an initial value. Also, you have CONSTANTs - they work exactly the same as VALUEs but you can't change their value once created (actually, that's not true, but we'll cover that another time!) 0 VALUE PX 0 VALUE PY 42 CONSTANT STAR : SETVAL 10 TO PY 5 TO PX ; : PCSET PY PX STAR 1 HCHAR ; : TEST SETVAL PCSET ; Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 18, 2011 Share Posted October 18, 2011 I noticed that figFORTH (specifically TI Forth) utilizes a number on the stack when initializing a variable... (n -- ) but there's nothing about that in your glossary. Since there was nothing about that in Starting FORTH by Brodie, I assumed TF got around that. FIG Forth takes an initial value for variables, but TF, which follows the Forth-83 standard, does not - it initialises its variables to 0. Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 18, 2011 Share Posted October 18, 2011 Regarding your last program, it was crashing due to stack underflow. You were consuming more stack items than you had on the stack. Remember that = consumes two arguments, so you need to DUP the output of KEY for each test in case the result of the test is false, then you still have the ascii code on the stack for the next test. Obviously, you don't need to do it for the last test. I've re-written your program the way I would do it. I've used constants for the key codes. They do consume memory, but there's no measurable performance penalty in using them. The slowest thing here by far is the actual scanning of the keyboard, which is done via the console ROM. CHAR E CONSTANT KEY_E CHAR X CONSTANT KEY_X CHAR S CONSTANT KEY_S CHAR D CONSTANT KEY_D : SETVAR 10 5 ; : TEST SETVAR BEGIN PAGE 0 0 GOTOXY .S 2DUP 42 1 HCHAR KEY DUP KEY_E = IF DROP SWAP 1- SWAP ELSE DUP KEY_S = IF DROP 1- ELSE DUP KEY_D = IF DROP 1+ ELSE KEY_X = IF SWAP 1+ SWAP THEN THEN THEN THEN AGAIN ; Hopefully you'll find the above nice and clear. Almost self-describing. A better version might be this, which doesn't rely on PAGE to clear the screen each time: : ERASE 2DUP 32 1 HCHAR ; : TEST SETVAR PAGE BEGIN 0 0 GOTOXY .S 2DUP 42 1 HCHAR KEY DUP KEY_E = IF DROP ERASE SWAP 1- SWAP ELSE DUP KEY_S = IF DROP ERASE 1- ELSE DUP KEY_D = IF DROP ERASE 1+ ELSE KEY_X = IF SWAP ERASE 1+ SWAP THEN THEN THEN THEN AGAIN ; Now we can use KEY? instead, which doesn't wait for a key: CHAR E CONSTANT KEY_E CHAR X CONSTANT KEY_X CHAR S CONSTANT KEY_S CHAR D CONSTANT KEY_D : SETVAR 10 5 ; : ERASE 2DUP 32 1 HCHAR ; : TEST SETVAR BEGIN 0 0 GOTOXY .S 2DUP 42 1 HCHAR KEY? DUP -1 <> IF DUP KEY_E = IF DROP ERASE SWAP 1- SWAP ELSE DUP KEY_S = IF DROP ERASE 1- ELSE DUP KEY_D = IF DROP ERASE 1+ ELSE KEY_X = IF SWAP ERASE 1+ SWAP THEN THEN THEN THEN ELSE DROP THEN AGAIN ; You might find it's a bit fast, and you'll also find you can fall off the top/bottom of the screen. We need to constrain the row and columns to the screen boundaries: CHAR E CONSTANT KEY_E CHAR X CONSTANT KEY_X CHAR S CONSTANT KEY_S CHAR D CONSTANT KEY_D : SETVAR 10 5 ; : ERASE 2DUP 32 1 HCHAR ; : CLIP-ROW 24 MOD ; : CLIP-COL XMAX MOD ; : TEST SETVAR PAGE BEGIN 2DUP 42 1 HCHAR KEY? DUP -1 <> IF DUP KEY_E = IF DROP ERASE SWAP 1- CLIP-ROW SWAP ELSE DUP KEY_S = IF DROP ERASE 1- CLIP-COL ELSE DUP KEY_D = IF DROP ERASE 1+ CLIP-COL ELSE KEY_X = IF ERASE SWAP 1+ CLIP-ROW SWAP THEN THEN THEN THEN ELSE DROP THEN AGAIN ; There you go. Quite easy to understand. Admittedly not as intuitive as XB, but it's not too bad. Also, it fits on a single block, and occupies 294 bytes. It would be less if it didn't use constants. However, I think constants make the code MUCH easier to read, and, if you want to change something, you only have to change your code in one place - the constant itself. I use them for character codes, colours, everything! E.g. 15 CONSTANT WHITE 42 CONSTANT FACE 20 VALUE ROW 40 VALUE COL 0 CONSTANT SPRITE#0 : AS-SPRITE 256 + DCHAR ; : FACE_DATA DATA 4 $7E81 $A581 $A599 $817E ; : TEST 1 GMODE FACE_DATA FACE AS-SPRITE SPRITE#0 ROW COL FACE WHITE SPRITE ; By the way, if you are using Firefox, you can just paste the above straight into Classic99, no need to use the editor! Enjoy. May the Forth be with you! Quote Link to comment Share on other sites More sharing options...
Opry99er Posted October 19, 2011 Share Posted October 19, 2011 Figured out last night that the VARIABLE PX VARIABLE PY has to be INSIDE a block but OUTSIDE a colon definition. I don't know why I didn't try that yesterday... Just kind of came to me this morning. =) Still having some issues however... I'm alot closer Block:006 0*VARIABLE PX VARIABLE PY 1*10 PX ! 5 PY ! 2*: GETVAR PY@ PX @ ; 3* 4*: TEST 5* BEGIN GETVAR 6* 2DUP PAGE 42 1 HCHAR 7* KEY 68 - 8* DUP 0 = IF DROP 1 + PX ! ELSE 9* DUP 1 = IF DROP SWAP 1 - PY ! ELSE 10* DUP 15 = IF DROP 1 - PX ! ELSE 11* DUP 20 = IF DROP SWAP 1 + PY ! ELSE 12* DROP 13* THEN THEN THEN THEN 14* AGAIN 15*; This is as close as I've gotten so far. It works the first several movements of the "*", but then freezes and an unending error tone fills the speakers. I'm thinking the stack might be going out of whack on me and I have no immediate way of telling... I tried to add a BREAK? in there to be able to bounce out and check the stack contents, but once the screen freezes up, I cannot even use BREAK? to get out. Also, it places the cursor at the top of the screen each time I move... I assume this is a product of "KEY"... I do not wish the cursor to appear each time the keyboard is waiting for input. Right now, I don't have internet access at home, so I am doing all my work on my laptop (I'm writing this message on my laptop) then I'm transferring pictures, text and videos to my thumbdrive and taking them to work to my work computer to post here. I don't have regular access to that computer either, so it's kind of touch and go. I have no idea whether you've answered my previous questions or not as I write this message. Anyway, I'm moving in the right direction I believe... Soon it'll be moving more quickly but the toughest thing is learning to think in Forth terms. Once you get used to the format and design of the language, the cloud kind of lifts. =) Thanks again, Mark. This is becoming a very nice journey. Quote Link to comment Share on other sites More sharing options...
Opry99er Posted October 19, 2011 Share Posted October 19, 2011 BTW thanks for your detailed responses above... I've copied them to my thumbdrive and I'll be reviewing and working through them tonight. I had a video of my issues but cannot seem to post it from my antiquated dinosaur of a computer here at work. =) You're the man Willsy! Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 19, 2011 Share Posted October 19, 2011 Block:006 0*VARIABLE PX VARIABLE PY 1*10 PX ! 5 PY ! 2*: GETVAR PY@ PX @ ; 3* 4*: TEST 5* BEGIN GETVAR 6* 2DUP PAGE 42 1 HCHAR 7* KEY 68 - 8* DUP 0 = IF DROP 1 + PX ! ELSE 9* DUP 1 = IF DROP SWAP 1 - PY ! ELSE 10* DUP 15 = IF DROP 1 - PX ! ELSE 11* DUP 20 = IF DROP SWAP 1 + PY ! ELSE 12* DROP 13* THEN THEN THEN THEN 14* AGAIN 15*; I think you have your thinking a bit mixed up in this version. I haven't tried your code. Just 'speed read' it, but it seems like you have two different ideas kind of mixed up. You are placing the row and column on the stack with 2DUP and then you store the values back in the variables. However, if you're using the stack to carry your row and column, there's no need for variables in this particular example. I'm fairly sure you are overflowing the stack (the stack has room for ~20 values) I would advocate a simpler, one or the other approach. Using just variables, it becomes just like any other programming language: You read a variable, you modify, and you write it back again. E.g. in BASIC: CALL KEY(0,K,S) IF K=68 THEN X=X+1 In Forth: KEY 68 = IF X @ 1+ X !THEN Or: KEY 68 = IF 1 X +! There's a slight difference, in that in BASIC, your key code is in a variable, and in the Forth examples above, the key code is on the stack, meaning that if you want to test the key code more than once then you need to DUP ("dupe") it so that it's still on the stack for subsequent tests. I can see from your code that you do understand this concept, however. However, if you wanted, you could simply put the key-code in a variable as well, and the Forth code suddenly becomes very BASIC like (and un-Forth like at the same time!) VARIABLE ROW 10 ROW ! VARIABLE COL 10 COL ! VARIABLE KEYCODE : TEST BEGIN ROW @ COL @ 42 1 HCHAR KEY? KEYCODE ! KEYCODE @ -1 <> IF ROW @ COL @ 32 1 HCHAR KEYCODE @ 68 = IF 1 COL +! THEN KEYCODE @ 83 = IF 1 COL -! THEN KEYCODE @ 69 = IF 1 ROW -! THEN KEYCODE @ 88 = IF 1 ROW +! THEN KEYCODE @ 32 = IF EXIT THEN ROW @ 24 MOD ROW ! COL @ XMAX MOD COL ! THEN AGAIN ; This is written in the style of a BASIC program. It works, and it's very fast (too fast, in fact). However, we can make it faster, and smaller (the above code is 248 bytes) by using the stack: VARIABLE ROW 10 ROW ! VARIABLE COL 10 COL ! : TEST BEGIN ROW @ COL @ 42 1 HCHAR KEY? DUP -1 <> IF ROW @ COL @ 32 1 HCHAR DUP 68 = IF DROP 1 COL +! ELSE DUP 83 = IF DROP 1 COL -! ELSE DUP 69 = IF DROP 1 ROW -! ELSE DUP 88 = IF DROP 1 ROW +! ELSE 32 = IF EXIT THEN THEN THEN THEN THEN ROW @ 24 MOD ROW ! COL @ XMAX MOD COL ! ELSE DROP THEN AGAIN ; Here, the keycode variable has been eliminated, the keycode is 'carried' on the stack. The reason it is faster is because a those DUPs and DROPs are much simpler machine code routines than the variable fetch routines. It's slightly smaller, at 242 bytes. Of course, it is possible to code the above with no variables at all - the 'pure' Forth way: : TEST 10 10 \ row and column BEGIN 2DUP 42 1 HCHAR KEY? DUP -1 <> IF -ROT 2DUP 32 1 HCHAR ROT DUP 68 = IF DROP 1+ ELSE DUP 83 = IF DROP 1- ELSE DUP 69 = IF DROP SWAP 1- SWAP ELSE DUP 88 = IF DROP SWAP 1+ SWAP ELSE 32 = IF 2DROP EXIT THEN THEN THEN THEN THEN XMAX MOD SWAP 24 MOD SWAP ELSE DROP THEN AGAIN ; This is, not to put too fine a point on it, is f***ing fast! The 'slow' part is the actual scanning of the keyboard. It's also far smaller, at 188 bytes. We keep the row and column on the stack with the 2DUP just after the BEGIN. Stack: row column Then we scan the keyboard, and DUP the keycode returned: Stack: row column keycode keycode Then we check to see if the keycode was -1 (no key pressed). The <> test consumes the top keycode (that's why we DUPd it). Stack: row column keycode If NO key was pressed, we enter the ELSE clause at the end of the code, and drop the keycode: Stack: row column And then we loop 'round again. If a key was pressed, then our stack looks like this: row column keycode We need to remove the star from the screen (by replacing it with a space). We need to get to the row and column on the stack, but the keycode is in the way. So, we use -ROT which rotates the top 3 stack items to the right. The items that WAS on the top now becomes the 3rd stack item: Stack: keycode row column Hey presto. We can get to the row and column. We now DUP the row and column, because HCHAR would consume them otherwise: Stack: keycode row column row column We now execute HCHAR to write a space. Stack: keycode row column Now we use ROT to move the top three items the other way: Stack: row column keycode Now we DUP the keycode, because we are going to perform a test on it, and the = word will consume it: Stack: row column keycode keycode We compare it to 68: Stack: row column keycode If it IS 68, we drop the keycode: Stack: row column And add 1 to the column with 1+ Stack: row column+1 This is repeated for the other keys. We don't need to DUP the keycode for the last test (our exit condition - the space bar) because we want it off the stack for the next time 'round, so we let = get rid of it for us. Then we just limit the row and column with MOD. We're done. Very fast, very Forthy, and very cool. Not intuitive though. There's no doubt, its harder than Basic. It's more like machine code. You have to follow the stack contents in your head, much like you would registers and condition bits in machine code. It's the code equivalent of Suduko. But it gets easier. For what it's worth: The code I wrote above didn't work first time, and I had to have a couple of hacks at it! I write my code in Notepad++ and just paste it into the command line of TF to test it. When it works, I save it in blocks! Hope this helps. Mark Quote Link to comment Share on other sites More sharing options...
Opry99er Posted October 20, 2011 Share Posted October 20, 2011 Just had the time to run through your examples... Very VERY good stuff, Mark! Using constants DOES make code alot more legible... and using VALUEs instead of variables is quite intuitive as well. I had already figured out the "outside-inside" colon definition issue you mentioned in your earlier response, but I have a better understanding of it now from reading your posts. I WAS using both concepts in that example and it was a mistake. =) Thanks for pointing it out for me and showing the alternatives. I am reminded of the line in Starting Forth quoting Charles Moore... something about factoring... Get Can of Tomatoes Open Can of Tomatoes Pour Can of Tomatoes Get Can of Mushrooms Open Can of Mushrooms Pour Can of Mushrooms In Forth, you factor out Get, Open, Pour, and Can Which factors DOWN to: ADD TOMATOES ADD MUSHROOMS Figuring out the things you use multiple times and creating a word so that your highest level definitions are human-readable... The washing machine example in that book... It comes together quite nicely when you start working with Forth at an actual terminal with an editor. And I very much enjoy the feel of TF. It feels much more natural than Wycove and is much easier to program than AceForth. I don't have much time today to program, but this weekend I plan to make a good go of writing the start of my Tidmouth Station game. (Willsy, you ought to know what that's about =)) I will need to figure out how to do screen-draws using VMBW while referencing a pre-made 768 byte list (probably produced from Magellan)... I have your glossary but will probably be needing a bit of a short tutorial on how to use file IO in TF. The glossary is a skeleton of the system, but a bit of fleshing out may be required for me. Also will need to learn to use SMLIST to send my engine and freight cars around the tracks. =) The glossary has the following stack effect: vdp_address cpu_address count -- I'm assuming it would be something like some address here CONSTANT MAPDAT1 : DRAW 0 MAPDAT1 768 VMBW ; the VMBW will hopefully read something like the following: DATA >2020,>4245,>5259,>4C20 ; DATA >2048,>5020,>3335,>2020 ; DATA >2020,>2020,>4C49,>5645 ; DATA >5320,>3220,>2020,>2020 ; * -- Map Row 1 -- DATA >2020,>2020,>2020,>2020 ; DATA >2A20,>202A,>2020,>2020 ; DATA >2020,>2020,>2020,>2A20 ; DATA >2020,>202A,>2020,>202A ; * -- Map Row 2 -- DATA >2020,>2020,>2020,>2020 ; DATA >2A20,>202A,>2020,>2020 ; DATA >2020,>2020,>2020,>2A20 ; DATA >2020,>202A,>2020,>202A ; * -- Map Row 3 -- DATA >2020,>2020,>2020,>2020 ; DATA >2A20,>202A,>2020,>2020 ; DATA >2020,>2020,>2020,>2A20 ; DATA >2020,>202A,>2020,>202A ; * -- Map Row 4 -- DATA >2020,>2020,>2020,>2020 ; DATA >2A20,>202A,>2A2A,>2A2A ; DATA >2A2A,>2020,>2020,>2A2A ; DATA >2A2A,>2A2A,>2020,>202A ; * -- Map Row 5 -- DATA >2020,>2020,>2020,>2020 ; DATA >2A20,>2020,>2020,>2020 ; DATA >202A,>2A2A,>2A2A,>2A20 ; DATA >2A20,>2A2A,>2020,>202A ; * -- Map Row 6 -- DATA >2A2A,>2A2A,>2020,>2020 ; DATA >2A20,>2020,>2020,>2020 ; DATA >2020,>2020,>2020,>2A2A ; DATA >202A,>202A,>2020,>202A ; * -- Map Row 7 -- DATA >2020,>202A,>2020,>2020 ; DATA >2A20,>2020,>2020,>2020 ; DATA >2020,>2020,>2020,>2A20 ; DATA >2A20,>2A2A,>2020,>202A ; * -- Map Row 8 -- DATA >2020,>202A,>2020,>2020 ; DATA >2A20,>202A,>2A2A,>2A2A ; DATA >2020,>2020,>2020,>2A2A ; DATA >202A,>202A,>2020,>202A ; * -- Map Row 9 -- DATA >2020,>202A,>2020,>2020 ; DATA >2A20,>202A,>2020,>202A ; DATA >2A2A,>2A2A,>2020,>2A20 ; DATA >2A20,>2A2A,>2020,>202A ; * -- Map Row 10 -- DATA >2023,>202A,>2020,>2020 ; DATA >2A20,>202A,>2020,>2020 ; DATA >2020,>202A,>2020,>2A2A ; DATA >202A,>202A,>2020,>202A ; * -- Map Row 11 -- DATA >2020,>202A,>2A2A,>2A2A ; DATA >2A20,>202A,>2020,>2020 ; DATA >2020,>202A,>2020,>2A20 ; DATA >2A20,>2A2A,>2020,>202A ; * -- Map Row 12 -- DATA >2020,>2020,>2020,>2020 ; DATA >2020,>202A,>2020,>2020 ; DATA >2020,>202A,>2020,>2A2A ; DATA >202A,>202A,>2020,>202A ; * -- Map Row 13 -- DATA >2023,>2020,>2020,>2020 ; DATA >2020,>202A,>2020,>2020 ; DATA >2020,>202A,>2020,>2A20 ; DATA >2A20,>2A2A,>2020,>202A ; * -- Map Row 14 -- DATA >2020,>202A,>2A2A,>2A2A ; DATA >2A23,>202A,>2020,>2020 ; DATA >2020,>202A,>2020,>2A2A ; DATA >2A2A,>2A2A,>2020,>202A ; * -- Map Row 15 -- DATA >2020,>202A,>2020,>2020 ; DATA >2A20,>202A,>2020,>2020 ; DATA >2020,>202A,>2020,>2020 ; DATA >2020,>2020,>2020,>202A ; * -- Map Row 16 -- DATA >2023,>202A,>2020,>2020 ; DATA >2A20,>202A,>2A2A,>2A20 ; DATA >2020,>202A,>2020,>2020 ; DATA >2020,>2020,>2020,>202A ; * -- Map Row 17 -- DATA >2020,>202A,>2020,>2020 ; DATA >2A20,>2020,>2020,>2A20 ; DATA >2020,>202A,>2A2A,>2A2A ; DATA >2A2A,>2A2A,>2020,>202A ; * -- Map Row 18 -- DATA >2020,>202A,>2020,>2020 ; DATA >2A20,>2020,>2020,>2A20 ; DATA >2020,>2020,>2A2A,>2A2A ; DATA >2A2A,>2A2A,>2020,>202A ; * -- Map Row 19 -- DATA >2A2A,>2A2A,>2020,>2020 ; DATA >2A2A,>2A2A,>2A20,>2A20 ; DATA >2020,>2020,>2A20,>2020 ; DATA >2020,>2020,>2020,>2A2A ; * -- Map Row 20 -- DATA >2020,>2020,>2020,>2020 ; DATA >2020,>2020,>2A20,>2A20 ; DATA >2020,>2020,>2A20,>2020 ; DATA >2020,>2020,>2020,>2A20 ; * -- Map Row 21 -- DATA >2020,>2020,>2020,>2020 ; DATA >2020,>2020,>2A20,>2A20 ; DATA >2020,>2020,>2A20,>2023 ; DATA >2020,>2020,>2020,>2A20 ; * -- Map Row 22 -- DATA >2020,>2020,>2020,>2020 ; DATA >2020,>2020,>2A20,>2A20 ; DATA >2020,>2020,>2A20,>2020 ; DATA >2020,>2020,>2020,>2A20 ; * -- Map Row 23 -- DATA >2020,>2020,>2020,>2020 ; DATA >2020,>2020,>2A20,>2A20 ; DATA >2020,>2020,>2A2A,>2A2A ; DATA >2A2A,>2A2A,>2A2A,>2A20 ; END As a newb, I'm quite dependent on examples to fill out my shaky understanding of certain aspects of Forth as a language. I have the TI Forth manual which gives solid examples on how to use file IO as it pertains to the TI-99 architecture--- but I'm pretty sure the syntax and operations are at least slightly different with TF. I assume that's where your book comes in. =) Again, I'm endebted to you. =) Byebye XB. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 20, 2011 Share Posted October 20, 2011 Very impressive, Mark! I realize I'm standing on the shoulders of giants, here; but, what about this: : TEST 10 10 \ row and column BEGIN 2DUP 42 1 HCHAR KEY? DUP -1 <> IF -ROT 2DUP 32 1 HCHAR ROT CASE 68 OF 1+ ENDOF 83 OF 1- ENDOF 69 OF SWAP 1- SWAP ENDOF 88 OF SWAP 1+ SWAP ENDOF 32 OF 2DROP EXIT ENDOF ENDCASE XMAX MOD SWAP 24 MOD SWAP ELSE DROP THEN AGAIN ; It's only 10 bytes longer at 198 bytes, but, it may be clearer---at least, it's different. ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 20, 2011 Share Posted October 20, 2011 Aha! Here it is at 188 bytes. Now, it's just a matter of taste: : TEST 10 10 \ row and column BEGIN 2DUP 42 1 HCHAR KEY? 1+ \ add 1 to make 0 if no key pressed ?DUP \ DUP if not 0 IF -ROT 2DUP 32 1 HCHAR ROT CASE \ check incremented ASCII values 69 OF 1+ ENDOF 84 OF 1- ENDOF 70 OF SWAP 1- SWAP ENDOF 89 OF SWAP 1+ SWAP ENDOF 33 OF 2DROP EXIT ENDOF ENDCASE XMAX MOD SWAP 24 MOD SWAP THEN AGAIN ; ...lee Quote Link to comment Share on other sites More sharing options...
+retroclouds Posted October 20, 2011 Share Posted October 20, 2011 Hi, If you guys are considering exporting data from Magellan, then please take a look at some of my last comments in the Magellan thread. There are some errors in the assembler data export. Values for color table don't match up and there was some other issue I can't recall right now. Anyway, I've fixed these bugs and there is an "unofficial" Magellan version attached in one of the last posts of that thread. Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 20, 2011 Share Posted October 20, 2011 Aha! Here it is at 188 bytes. Now, it's just a matter of taste: : TEST 10 10 \ row and column BEGIN 2DUP 42 1 HCHAR KEY? 1+ \ add 1 to make 0 if no key pressed ?DUP \ DUP if not 0 IF -ROT 2DUP 32 1 HCHAR ROT CASE \ check incremented ASCII values 69 OF 1+ ENDOF 84 OF 1- ENDOF 70 OF SWAP 1- SWAP ENDOF 89 OF SWAP 1+ SWAP ENDOF 33 OF 2DROP EXIT ENDOF ENDCASE XMAX MOD SWAP 24 MOD SWAP THEN AGAIN ; ...lee He he! Very nice sir! Indeed, CASE is your friend. The only reason I didn't mention it (though I think I mentioned earlier up in the thread) is that I figure that Owen isn't ready for it yet - I think he's still thinking first in BASIC terms, and how that translates to Forth. As we know, it helps if you try not to think in terms of your previous experience with other languages, as Forth is so different. The trick with adding 1 to the keycode is nice! I think I used that somewhere inside the TF Kernal if I remember correctly. Probably with the word WORDS. But yep, for sure, CASE is the way to go for me. I think the rule is "When you have a CASE of checking the same value against a list, it just a CASE of using CASE!" Man, I'm good Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 20, 2011 Share Posted October 20, 2011 Hi, If you guys are considering exporting data from Magellan, then please take a look at some of my last comments in the Magellan thread. There are some errors in the assembler data export. Values for color table don't match up and there was some other issue I can't recall right now. Anyway, I've fixed these bugs and there is an "unofficial" Magellan version attached in one of the last posts of that thread. Thanks, Retroclouds, good call. Owen, with regard to your screen data for your levels, there are (as there always are in forth) many ways to skin the cat (poor old cat). I think the way we will go, is to simply use the block editor to, quite literally, draw your screen! Your screens will sit inside blocks. If your train tracks are no deeper than 16 screen lines, it's really really simple: Just draw them using the ASCII characters that define them (or, use a substitute, and we'll translate the data on the fly as we draw it). If your level data is deeper than 16 lines, then you can still fit it on one block: A block is 64 characters wide, so it will hold exactly two lines of 32 column screen data. In that case, characters 32 to 64 on line 0 of the block would be line 17 of your track. Characters 32 to 64 on line 1 would be line 18 of your track etc. Hope that makes sense. By doing it that way, you don't need pages and pages of data (that take up space in CPU ram, as they are loaded with your code and sit in memory). Your track data, being in a block, will effectively take up no RAM at all. Nothing. BLOCK IO in TF is also pathetically easy. There are a suite of words associated with Block IO (far more than are mandated in the Forth 83 standard) so it's quite powerful. Also, remember that in TF, block data lives in VDP memory - you can have up to six blocks in memory. When you ask TF to go get a block, it first checks its '6 block cache' and if it is already in memory then it won't even go to the disk drive - it will be instant. This means you could, if you wanted to, load 6 levels at a time, with effectively no memory penalty (they are in VDP ram) and no run-time penalty, once they are loaded. Hope this all makes sense; when you are ready let me know. Hang in there - you're about to go on a great journey! And Lee (and anyone else), please feel free to chip in as you like! Mark Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 20, 2011 Share Posted October 20, 2011 Hi, If you guys are considering exporting data from Magellan, then please take a look at some of my last comments in the Magellan thread. There are some errors in the assembler data export. Values for color table don't match up and there was some other issue I can't recall right now. Anyway, I've fixed these bugs and there is an "unofficial" Magellan version attached in one of the last posts of that thread. For the benefit of everyone, the magellan thread is here: http://www.atariage.com/forums/topic/161356-magellan/page__st__250 It should be perfectly possible to develop the screens in magellan, too, and write a 'pre-processor' in TF to process and import the data into blocks. Quote Link to comment Share on other sites More sharing options...
Opry99er Posted October 20, 2011 Share Posted October 20, 2011 Yes Mark... I VERY much like the "Forthy" one (of your examples from yesterday)... But, as you said, not very intuitive. It is the type of code that shows seasoned skill with the language. I would, of course, like to eventually be able to code in that fashion--- It is what makes Forth so "nifty." It's almost like a waltz... You "dance" with the stack as you manipulate it--- Good dancers won't step on toes... =) For me... a square dance is probably the best way to start. Blocky VALUEs and straight-forward, BASIC-styled code segments will give me the start I need before I learn more intricate steps. Thank you for the step-by-step of the stack contents as well... That actually helped quite a bit. I have a feeling that AceForth and FIGnition are primarily designed to work the "pure-Forth way". Block:006 0*VARIABLE PX VARIABLE PY 1*10 PX ! 5 PY ! 2*: GETVAR PY@ PX @ ; 3* 4*: TEST 5* BEGIN GETVAR 6* 2DUP PAGE 42 1 HCHAR 7* KEY 68 - 8* DUP 0 = IF DROP 1 + PX ! ELSE 9* DUP 1 = IF DROP SWAP 1 - PY ! ELSE 10* DUP 15 = IF DROP 1 - PX ! ELSE 11* DUP 20 = IF DROP SWAP 1 + PY ! ELSE 12* DROP 13* THEN THEN THEN THEN 14* AGAIN 15*; Looking back at the above code, what I did was just combine my straight Forth example from a previous post with the VARIABLE concept without really modifying it. I see clearly that the stack was overflowed--- the 2DUP was used for the example without variables and wasn't removed when the variables were added. I think I'll go with VALUEs now, though... Since I know they are available and how they're used... much cleaner. =) (again, apologies for the messed up formatting in the below test code) 10 VALUE PX 5 VALUE PY : TEST BEGIN PY PX 2DUP 42 1 HCHAR KEY? DUP -1 <> IF -ROT 2DUP 32 1 HCHAR ROT DUP 68 = IF DROP 1 +TO PX ELSE DUP 69 = IF DROP -1 +TO PY ELSE DUP 83 = IF DROP -1 +TO PX ELSE DUP 88 = IF DROP 1 +TO PY ELSE THEN THEN THEN THEN ELSE DROP 2DROP THEN AGAIN ; 1) defined values for PX and PY (-) 2) begin loop and add PY and PX to stack (PY PX) 3) 2DUP the values, add 42 and 1, execute HCHAR (PY PX) 4) Check for KEY, if -1 (no key pressed) skip to ELSE (PY PX KEY) 5) If no key pressed, DROP KEY, 2DROP PX PY (-) 6) Loop back to BEGIN, adding PY and PX again (PX PY) 7) If KEY is <>-1 (meaning key pressed) perform -ROT (KEY PY PX) **This particular function I VERY much like 2DUP PX and PY and add 32 and 1, execute HCHAR, erasing the * (KEY PY PX) 9) ROT KEY to top (PX PY KEY) 10) Begin checks by DUPing the key each check 11) Instead of using + and ! (store) (variable), a +TO will modify my VALUE I think I have this right, Willsy... I still don't have screen parameters in this example, but it works nicely AND I'm avoiding using variables. This sh** makes me salivate, man. =) I don't really know how to describe the effect it has on my brain, but it's pretty consuming. I'm sure you understand what I mean. I have no desire to use line numbers anymore. This crazy reverse-Polish, stack-based language has me pretty much wound up. The inventor of FIGnition is sending me a machine with some experimental NTSC firmware to try out... I'm buying the system from him--- but it's only $35 USD shipped. =) It's quirky and interesting to me, so I thought I'd buy one. Sunday will be a Forth day for me... I've "block"ed out a few hours to work on my Forthing. If I get started on my game idea, great... but I'll probably be exploring and exercising my itchy fingers. =) Wish me luck buddy. =) Quote Link to comment Share on other sites More sharing options...
Opry99er Posted October 20, 2011 Share Posted October 20, 2011 Sorry if this is an extremely elementary question, but.... XMAX MOD SWAP 24 MOD SWAP I understand that this checks (using MOD) whether "something" is above XMAX (which is the maximum column value for the current GMODE) I also understand that 24 MOD checks for the row... but how is the ROW indicated in this line? In BASIC, It would be something like IF PY>24 THEN PY=24 This would hold the character at 24 as long as "down" is pressed... How does XMAX MOD SWAP 24 MOD SWAP check for WHERE the star is in this case... I guess I understand in principle and it would be easy to put this into my above code (where I used VALUEs instead of VARIABLEs) but I'd like to know exactly how/why it works before I add it into my code... Thanks in advance. =) Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 20, 2011 Share Posted October 20, 2011 (edited) Owen... I hope you don't mind my jumping in here. Regarding XMAX MOD SWAP 24 MOD SWAP Some of this I am sure you know, but tracking through it, before this line is executed in Willsy's example, the stack contains the row and column: ( 5 10 ). XMAX adds the screen width (say 40) to the top of the stack ( 5 10 40 ). MOD divides the top number on the stack into the next one down and leaves the remainder on the stack ( 5 10 ). SWAP gets the row to the top of the stack ( 10 5 ). 24 is added ( 10 5 24 ) MOD executes again, taking the top two numbers and leaving the remainder ( 10 5 ). SWAP retores the row-column order ( 5 10 ), which is now ready for the next go round. What the above code does is to wrap around from one edge of the screen to the other, such that, if you hold one key down, the '*' will continue moving in that direction (with wrapping) until you release the key, rather than banging into the edge and staying there. Also, since you wish to use VALUEs instead of the faster and more compact stack manipulations, you don't need much of the DUP, ROT, SWAP and -ROT code. Here is your last example with those removed and making full use of PX and PY: 10 VALUE PX 5 VALUE PY : TEST BEGIN PY PX 42 1 HCHAR KEY? DUP -1 <> IF PY PX 32 1 HCHAR DUP 68 = IF DROP 1 +TO PX ELSE DUP 69 = IF DROP -1 +TO PY ELSE DUP 83 = IF DROP -1 +TO PX ELSE DUP 88 = IF DROP 1 +TO PY ELSE 32 = IF EXIT THEN THEN THEN THEN THEN PY 24 MOD TO PY PX XMAX MOD TO PX ELSE DROP THEN AGAIN ; FYI, this takes 238 bytes of dictionary space, which is 25 % more space than Willsy's or my stack-based code; but, it is still very fast, indeed! ...lee Edited October 21, 2011 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
Opry99er Posted October 21, 2011 Share Posted October 21, 2011 Thanks alot Lee... that made perfect sense. =) Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 21, 2011 Share Posted October 21, 2011 Yeah! What Lee said (it's good to have someone else here that can answer the Forth questions - takes some of the pressure off - Thanks Lee!) Owen, to illustrate the use of MOD, try this: : TEST 1000 0 DO I XMAX MOD . LOOP ; See? The value displayed never goes above 39, because we are taking a REMAINDER of a division. This is very neat for wrap around effects and stuff and like that. Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 21, 2011 Share Posted October 21, 2011 IF PY>24 THEN PY=24 The equivalent in Forth would be: SWAP DUP 23 > IF DROP 23 THEN SWAP ; You need the swaps because the row is underneath the column on the stack. For the column, it's simpler: DUP 39 > IF DROP 39 THEN For example, if I wanted, say, Lee's routine, and didn't want wrap around, then I could go for this: 10 VALUE PX 5 VALUE PY : TEST BEGIN PY PX 42 1 HCHAR KEY? DUP -1 <> IF PY PX 32 1 HCHAR DUP 68 = IF DROP 1 +TO PX ELSE DUP 69 = IF DROP -1 +TO PY ELSE DUP 83 = IF DROP -1 +TO PX ELSE DUP 88 = IF DROP 1 +TO PY ELSE 32 = IF EXIT THEN THEN THEN THEN THEN \ do limit checks PY 23 > IF 23 TO PY THEN PY 0 < IF 0 TO PY THEN PX XMAX = IF XMAX 1- TO PX THEN PX 0 < IF 0 TO PX THEN ELSE DROP THEN AGAIN ; That should work fine. We're quite used to seeing code like this in XB. However, it annoys me that the code is checking both the row AND the column each time through the loop, when we can only move up/down OR left/right each time through the loop (we can't do diagnals with the keyboard). Even worse, if the star is, say, on the far right of the screen, then it cant possibly be on the far left of the screen, so why check both? (Same for the row, if it's at the top then it can't be at the bottom, so why check?) It would be faster to put the appropriate checks with the code that does the movement: 10 VALUE PX 5 VALUE PY : TEST BEGIN PY PX 42 1 HCHAR KEY? DUP -1 <> IF PY PX 32 1 HCHAR DUP 68 = IF DROP PX XMAX 1- < IF 1 +TO PX THEN ELSE DUP 69 = IF DROP PY 0 > IF -1 +TO PY THEN ELSE DUP 83 = IF DROP PX 0 > IF -1 +TO PX THEN ELSE DUP 88 = IF DROP PY 23 < IF 1 +TO PY THEN ELSE 32 = IF EXIT THEN THEN THEN THEN THEN ELSE DROP THEN AGAIN ; As you can see, when we want to go, say, downwards, we check the row first, and if we're not on the bottom line already we allow it, otherwise we leave the row unchanged. The point is, *only* one check is done per direction, instead of four, making it potentially 4 times faster, and measurably (if you have some way of measuring it!) faster. You're probably already familiar with tese types of optimizations, but I thought I'd point it out for the benefit of the lurkers! 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.