Jump to content


+AtariAge Subscriber
  • Content Count

  • Joined

  • Last visited

Everything posted by TheBF

  1. VDP Strings exploration ​​Continuing with our VDP memory management tools, we can go further and make strings that are similar to BASIC. ​And just for fun we will add a few commands to make using the string look more like BASIC too. Lets make a new command call "DIM" that takes a size input number and text name and makes space for string in VDP memory: : DIM ( n -- ) CREATE VHERE , \ compile time: compile the DP address into the Forth word VALLOT \ allocate 'n' bytes of VDP ram. DOES> @ ; \ run time: fetch the VDP address ​ ** EDIT ** ​Although the code above works just fine, it is not optimal. For those whose looked closely you can see that DIM is actually a constant the gives us ithe address of VHERE at compile time, ​with the addition of VALLOT. Forth systems typically optimize the word CONSTANT in assembler to get a value to the top of stack very fast. ​CAMEL99 Forth is no exception so... a faster implementation is to do this: : DIM ( size -- ) VHERE CONSTANT VALLOT ; We store VHERE as a fast constant and allocate the size from the number of the stack in VDP RAM. Testing showed the 1st version takes 210uS when compiled. The second version takes 105uS. Much better performance and we save space too!. We can also use a VDP version of the Forth word COUNT which takes a counted string address and extracts the length and adds 1 to the address to point to the start of the text. So here is VCOUNT. The only difference form the standard word is VCOUNT uses [email protected] to read the length byte from VDP RAM. This we be used for printing strings. : VCOUNT ( vdp$adr -- vdpadr len ) DUP 1+ SWAP [email protected] ; \ convert counted string to addr,len So to make a string the syntax is now: 80 DIM A$ 80 DIM B$ Not quite BASIC but you could work with it. We need a way to get text into these strings. The standard word PLACE takes an address and length of a string and "PLACEs" it at a 2nd address as a counted string. We will make VDP version called VPLACE. : VPLACE ( $adr len VDP-adr -- ) 2DUP VC! CHAR+ SWAP VWRITE ; \ place $adr len in VDP-adr So VPLACE will take a string in CPU RAM and PLACE it in VDP ram. Now for a little syntax candy. We can PARSE the console input with word called ... PARSE .. what else? PARSE returns Address and length of the parsed string. How convenient! Let's do this: \ collect characters up to final " and put in VDP memory : =" ( VDP$ -- ) [CHAR] " PARSE ROT VPLACE ; So now we have made syntax for working with our VDP strings that looks like this: 80 DIM A$ 80 DIM B$ A$ =" Now is the time for all good men " B$ =" to come to the aid of their country." And finally we need to print these strings. Forth has word called TYPE so we can just emulate that word, but read the bytes from VDP RAM. VTYPE takes the address and length, calculates the end and start address of the string with "BOUNDS" and then loops through the addresses, reading a VDP byte and EMITing to the screen. : VTYPE ( vdp_addr len -- ) BOUNDS ?DO I [email protected] EMIT LOOP ; To continue with BASIC syntax emulation we can make a word called PRINT, that starts on a newline. : PRINT CR VCOUNT VTYPE ; So with these words we have a simple string system in Forth that puts strings in VDP RAM. The GIF shows how it all works then prints A$ and B$ 100 times.
  2. Well of course these things make the programming look pretty, but there are overheads and I have the luxury of working with an unfinished system. With all the permutations you mention for the VDP modes it is probably best dealt with as you have done it. It's not truly a free block of memory that you can use and release. But if you lock the Graphics down like BASIC does these are great features to have in a TI-99 language. And for tutorial purposes it does show how one can extend Forth to work with different kinds of memory in a system. Part of my exploring this was to answer a personal question that I asked after I discovered Forth on the on 99 in the 1980s. Could TI have made a more responsive system if they had built it in Forth with code primitives? From what I see your work and Mark's and my own experiments, it would have been faster than GPL. That said to make it easily hook to other languages it might have required a byte code Forth system, which makes it one level slower than ITC but I think the virtual instructions per second would still be faster than GPL seems to do and with fast code primitives to support it I am pretty much convinced it would be faster. All that deep thinking plus 5 bucks is enough to get you a big fancy coffee at Starbucks. :-)
  3. Creating VDP Constants ​Now that we know how to make a VDP variable in Forth it becomes trivial to create a constant that is stored in VDP RAM. A variable remember, records a '0' in VDP RAM and simply returns the address where it is stored. So VDP constant would need to take an input number and store that rather than '0' and... it would need to get that value and return it to Forth when we use it in a program. Here's how we do it. : VCONST: ( n -- <text>) CREATE VHERE , ( n) V, \ compile time: VDP address in CPU RAM, put n in VDP RAM DOES> @ [email protected] ; \ run time: fetch the VDP address, \ then fetch the const from that VDP address ​The screen capture shows how we could use them.
  4. Managing other TI-99 Memory Spaces Using these concepts it becomes super easy to make a set of Forth words to manage the VDP memory as well. All we need to begin is a VARIABLE to keep track of the last used memory address and then we create a set of words like we have for CPU RAM but built for VDP RAM. Here is how that could look. \ Simple VDP RAM manager \ Works the same way as the Forth dictionary \ Best for static allocation VARIABLE VP \ VDP memory pointer (4K block) HEX 1000 VP ! \ set the start of free VDP RAM : VHERE ( -- addr) VP @ ; \ FETCH the value in VDP pointer : VALLOT ( n -- ) VP +! ; \ add n to the value in VDP pointer Now we have VHERE which gives us the next free VDP address and VALLOT which lets us allocate VDP memory. Super simple. So what can we do with that? We can create our little number compilers like we made for Forth CPU memory. We will call them V, and VC, so we remember that they only work in VDP memory. \ VDP number "compilers". Put a number or character in VDP RAM \ and advance the VP pointer by the proper amount (2 or 1) : V, ( n -- ) VHERE V! 2 VALLOT ; : VC, ( n -- ) VHERE VC! 1 VALLOT ; So what does that give us? Well how about creating VDP variables? : VAR: ( -- <text>) \ accessed with [email protected] and V! CREATE VHERE , \ compile time: compile VHERE address in CPU RAM, ​ 0 V, \ put '0' in VDP RAM at that address DOES> ( -- addr) \ run time: DOES> returns the address after create ​ @ ; \ so we fetch the contents of that address, ​ \ which is the VDP address we stored there at compile time. This uses a feature of Forth that can make some people confused. Ask questions if you need help. (hint: If you know object oriented programming, this is like creating an object that has only one method) The new word VAR: above creates new Forth words. This has 2 aspects: 1. What happens when we create the new word in the dictionary? This is called "compile time action" 2. What happens when the word we created actually runs in a program. This is called "run time action" #1 is handled by the code after the word CREATE. #2 is handled by the code after the word DOES> (in other words what the word "does" when you run it) So read the code for VAR: and see that after it "creates" a word in the dictionary, it compiles VHERE into Forth memory and then... it compiles a '0' into VHERE. which is a VDP address remember. (read that again if you need to). The word is recording 2 pieces of information: The VDP address where it put something AND it puts a zero in that VDP address. Usage of VAR: VAR: X VAR: Y VAR: Z So we have made 3 new Forth words X,Y and Z. When we type X, Y, or Z at the Forth console, or use then in a program what happens? The "DOES>" code will run for each one of them! When DOES> runs it returns the address in Forth (CPU) ram right after the name of the word. This is exactly where we put the VHERE address! How convenient! So all we need to do is 'fetch' what is in the address using the Forth word "@" and that returns to us an address in VDP RAM. That's what a Forth variable does. Gives us an memory address so we can put something there or read it. CAMEL99 has the word [email protected] which reads 16 bits from VDP RAM and V! which stores 16 bits in VDP RAM. These are like @ (peek) and ! (poke) in Forth memory space. ( Please note: TURBOFORTH also has [email protected] but it reads 1 byte, not 2 bytes as in CAMEL99 Forth ) ( CAMEL99 has [email protected] to read 1 byte from VDP RAM. ) So to use our new "VDP VARS" we would say: DECIMAL 9 X V! 1 Y V! X [email protected] Y [email protected] + Z V! Z [email protected] . Next we will create named VDP buffers and VDP strings.
  5. I have not tried. I am waiting for al the smoke to clear in this thread.
  6. ./elf.texi:11: raising the section level of @subsubsection which is too low make[3]: *** [Makefile:394: bfd.info] Error 1 make[3]: Leaving directory '/cygdrive/c/Users/Brizio/Retro/gcc-installer/build/binutils-2.19.1/bfd/doc' Making info in po make[3]: Entering directory '/cygdrive/c/Users/Brizio/Retro/gcc-installer/build/binutils-2.19.1/bfd/po' ( if test 'x../.././bfd/po' != 'x.'; then \ posrcprefix='../.././bfd/'; \ else \ posrcprefix="../"; \ fi; \ rm -f SRC-POTFILES-t SRC-POTFILES \ && (sed -e '/^#/d' \ -e '/^[ ]*$/d' \ -e "[email protected]*@ $posrcprefix& \\\\@" < ../.././bfd/po/SRC-POTFILES.in \ | sed -e '$s/\\$//') > SRC-POTFILES-t \ && chmod a-w SRC-POTFILES-t \ && mv SRC-POTFILES-t SRC-POTFILES ) ( rm -f BLD-POTFILES-t BLD-POTFILES \ && (sed -e '/^#/d' \ -e '/^[ ]*$/d' \ -e "[email protected]*@ ../& \\\\@" < ../.././bfd/po/BLD-POTFILES.in \ | sed -e '$s/\\$//') > BLD-POTFILES-t \ && chmod a-w BLD-POTFILES-t \ && mv BLD-POTFILES-t BLD-POTFILES ) cd .. \ && CONFIG_FILES=po/Makefile.in:po/Make-in \ CONFIG_HEADERS= /bin/sh ./config.status I have heard people say that Forth is a read only language... But all joking aside, installing Tursi's GCC port is worth enduring all the pain of this holy-crap complex installation. From what we have seen it generates excellent 9900 code. Well... you know, for a C compiler. (ok just a little joking)
  7. Is this because you are using an FPGA library ALU that is more efficient ?
  8. Thanks for this link. I was not aware of this method but I like it alot.
  9. Hey Lee, When you post Forth code, what language do you select to make the colors look like that?
  10. Here is how I rendered it in Camel99 Forth \ classic pipe & bar illusion by sometimes99er on Atariage GRAPHICS HEX CREATE SHAPES 0102 , 0408 , 1020 , 4080 , 8040 , 2010 , 0804 , 0201 , 8080 , 8080 , 8080 , 8080 , 80C0 , A090 , 8884 , 8281 , 8142 , 2418 , 1020 , 4080 , 0302 , 0408 , F000 , 0000 , E010 , 0804 , 0402 , 0202 , 0102 , 0408 , 0810 , 1010 , 1008 , 0804 , 0300 , 0000 , \ write character definitions to VDP RAM all at once : CHARACTERS ( 'pattern ascii# char-cnt -- ) 8* SWAP ]PDT SWAP VWRITE ; DECIMAL SHAPES CHAR 0 9 CHARACTERS : RUN PAGE CR ." 01" CR ." 0 1" CR ." 0 03 1" CR ." 0 0 21 1" CR ." 0 0 010 02" CR ." 0 0 0 0 0 2" CR ." 0 0 0 0 0 0" CR ." 0 0 0 0 0 0" CR ." 0 0 0 0 0 0" CR ." 760 0 0 0 0 " CR ." 85 0 0 0 0" CR ." 760 0 0" CR ." 85 0 0" CR ." 760" CR ." 85" BEGIN KEY? UNTIL ;
  11. That is really cool. Now I have to figure out how you did it!
  12. I can see the effect more on this one with green. Pretty nice job what you did in 8 lines.
  13. I got no effect on a small window so I tried full screen and then I get just the beginning of the effect. (but very slight)
  14. Sorry dude. I completely missed that one. I did not run it and saw this other image on a youtube video. You got there first.
  15. Thanks for this. Apologies for not replying faster. You are correct, I could play with the numbers to get what I want. I have been using a multi-tasking system for my Forth system and I re-created the ISR in a little task. Forth handles all this stuff like Assembler so I too use CPU RAM for my position and motion tables and the write the block of sprite positions to VDP RAM all at once. So I am on the right track there at least. :-) I have been reviewing the ISR code and looking at the debugger to see how it works. Here is what I think I see: 1. The Motion values are signed bytes. Positive: 0 to 7F, Negative: 80 to FF 2. The Aux values, beside each motion pair, are incremented every tick of the interrupt, by the amount in the motion value These are the little counters that run for each sprite's X and Y value. 3. When the AUX value rolls over the sprite is moved 1 pixel. So bigger values of Motion cause the AUX register to rollover faster of course therefore those sprite moves faster. So at this stage I am debating how best to handle this in Forth just to see what I can do without writing an ASM version. Forth deals with ints better than bytes so I am thinking that I will use 16 bit tables for motion and timers (AUX) and just shift the motion values to upper byte. Since everything is multiplied by >100 it will all count the same speed as using bytes, but my Forth operators will be faster for 16 bit values. That's all I have at this time. Thanks again for confirming my thoughts on the matter.
  16. You're a mutant. Call professor X
  17. Maybe you can figure out how to do this one. It seems tricky to do with characters. There is a dot at every intersection, but I can only see 3 at time no matter how hard I look!
  18. Yes. That's fun. And I think there is one that is even weirder using that template... I will try it
  19. A 2nd Shade of Gray for TI-BASIC ​ 100 CALL CLEAR 110 CALL SCREEN(16) 120 PAT$="FFFFFFFFFFFFFFFF" 130 CALL CHAR(96,PAT$) 140 CALL CHAR(104,PAT$) 150 CALL COLOR(9,2,1) 160 CALL COLOR(10,15,1) 170 CALL HCHAR(2,9,65) 180 CALL HCHAR(2,23,66) 190 FOR I=4 TO 24 STEP 2 200 CALL HCHAR(I,1,96,32) 210 NEXT I 220 LET COL=7 230 LET ROW=5 240 GOSUB 290 250 LET COL=20 260 LET ROW=6 270 GOSUB 290 280 GOTO 250 290 FOR X=ROW TO 24 STEP 2 300 CALL HCHAR(X,COL,104,6) 310 NEXT X 320 RETURN
  20. My first FB Forth program: I cheated a little bit. I took a CAMEL99 program and added a small set of translation words. And guess what? It works just fine. For those who are just learning Forth it demonstrates how to use the interrupt timer for a precise delay and how to use the number formatting words. Just paste this code into FB Forth and it puts little clock on the screen. \ A simple Clock in FB Forth (FIG Forth dialect) \ Tiny HARNESS for FBFORTH->CAMEL99 translation : 2DROP DROP DROP ; : 2DUP OVER OVER ; : VARIABLE 0 VARIABLE ; : AT-XY ( n n -- ) GOTOXY ; : OFF ( addr -- ) 0 SWAP ! ; : ^C? ?TERMINAL ; \ CAMEL99 version can now compile HEX 8379 CONSTANT TICKER DECIMAL VARIABLE HRS VARIABLE MINS VARIABLE SECS VARIABLE TICKS 58 CONSTANT ':' \ character we will need \ increment no. in the address : 1+! ( addr -- ) 1 SWAP +! ; \ FB FORTH and CAMEL99 Forth run with interrupts enabled \ so the ticker is changing every 1/60 of a second. \ We can wait until ticker changes and know it's 1/60 of a second : 1/60 ( -- ) TICKER DUP @ \ dup ticker & fetch initial timer value BEGIN OVER @ \ read it again OVER - \ subtract initial value UNTIL \ loop until result is not zero 2DROP ; \ drop the initial value : 1SEC ( -- ) 60 0 DO 1/60 LOOP ; : KEEPTIME 1SEC SECS 1+! SECS @ 60 = IF SECS OFF MINS 1+! THEN MINS @ 60 = IF MINS OFF HRS 1+! THEN ; \ formatted output : ## ( n -- ) 0 <# # # #> TYPE ; : :## ( n -- ) 0 <# # # ':' HOLD #> TYPE ; : .TIME ( -- ) SECS @ MINS @ HRS @ ## :## :## ; : SETCLOCK ( hrs mins secs -- ) SECS ! MINS ! HRS ! ; : CLOCK ( row col -- ) BEGIN 2DUP AT-XY .TIME KEEPTIME ^C? \ waits for function clear in FB Forth UNTIL 2DROP ; 1 59 50 SETCLOCK PAGE 10 10 CLOCK
  21. Thanks Lee. That's a much nicer looking listing!
  22. There are Sprites in the air it seems. I stayed up late last night trying to grok the ISR code for Sprite motion. My homemade sprite auto-motion system is wrong, but I don't understand exactly how. I was taking a velocity(x,y) (+ or - ) from an array and simply adding it to the current sprite position. This gave very ragged motion. I checked XB and the sprites are only moved 1 pixel at a time from what I can see regardless of the velocity used. (Classic99 debugger) So if anybody can explain what's going on with this ROM code I would be all ears. Like what is this auxiliary data in the motion table? This is from TI Intern, but I have added some my own comments to help me stay on track 093A C002 MOV 2,0 093C C0A2 MOV @>0002(2),2 Fetch INT address 093E 0002 0940 0692 BL *2 And execute 0942 C090 MOV *0,2 Next Int routine 0944 10F9 JMP >0938 0946 0460 B @>0AB8 End interrupt from CRU 0948 0AB8 094A 1D02 SBO >0002 Clear VDP interrupt 094C D060 MOVB @>83C2,1 Fetch interrupt flag byte 094E 83C2 0950 0A11 SLA 1,1 No interrupt permitted 0952 1702 JNC >0958 0954 0460 B @>0A84 then jump 0956 0A84 0958 0A11 SLA 1,1 095A 1846 JOC >09E8 No sprite move permitted, then jump 095C D320 MOVB @>837A,12 Number sprites -> R12 095E 837A 0960 1343 JEQ >09E8 IF sprite_cnt=0 then skip all this 0962 098C SRL 12,8 Move sprite count to other byte 0964 0202 LI 2,>8800 VDP RD -> R2 0966 8800 0968 0203 LI 3,>8C00 VDP WD -> R3 096A 8C00 096C 0208 LI 8,>0780 Sprite motion table -> R8 096E 0780 * begin loop for each sprite 0970 D7E0 MOVB @>83F1,*15 Write address motion table 0972 83F1 0974 D7C8 MOVB 8,*15 load Spr motion table to VDP Write address *===================================================== 0976 04C4 CLR 4 0978 D112 MOVB *2,4 read Motion table Y velocity ->R4 097A 04C6 CLR 6 097C D192 MOVB *2,6 read Motion table X X velocity->R6 097E 0844 SRA 4,4 ?? shift Y velocity right 4 0980 D152 MOVB *2,5 read Y Auxiliary sprite data->R5 0982 0845 SRA 5,4 shift Aux data 4 right (16/) 0984 A144 A 4,5 ADD R4->R5 *==================================================== 0986 D1D2 MOVB *2,7 Read Y Auxiliary sprite data-> R7 0988 0846 SRA 6,4 Shift X motion by 4 right 098A 0847 SRA 7,4 Shift X aux data 4 right 098C A1C6 A 6,7 Add R6->R7 098E 0228 AI 8,>FB80 Address sprite descriptor table 0990 FB80 0992 D7E0 MOVB @>83F1,*15 Write address 0994 83F1 0996 D7C8 MOVB 8,*15 ==================================================== 0998 04C4 CLR 4 099A D112 MOVB *2,4 Fetch Y position->R4 099C A105 A 5,4 add Y motion+aux+Ypos->R4 099E 0284 CI 4,>C0FF are we at X,Y limits (192,256) 09A0 C0FF 09A2 1209 JLE >09B6 if not off screen, goto 9B6 09A4 0284 CI 4,>E000 are we at start of screen ( 224,00) 09A6 E000 09A8 1B06 JH >09B6 if HI goto 9b6 09AA C145 MOV 5,5 else test for 0 09AC 1502 JGT >09B2 if R5>0 goto 09B2 09AE 0224 AI 4,>C000 ?? what is this for 09B0 C000 09B2 0224 AI 4,>2000 ?? and why add this 09B4 2000 09B6 04C6 CLR 6 *screen limit tests 09B8 D192 MOVB *2,6 09BA A187 A 7,6 09BC 0268 ORI 8,>4000 setup VDP address for writing 09BE 4000 09C0 D7E0 MOVB @>83F1,*15 09C2 83F1 09C4 D7C8 MOVB 8,*15 09C6 D4C4 MOVB 4,*3 Write X,Y positions to Spr. desc. table 09C8 0228 AI 8,>0482 ? 09CA 0482 09CC D4C6 MOVB 6,*3 09CE 06C5 SWPB 5 09D0 D7E0 MOVB @>83F1,*15 Write address motion table 09D2 83F1 09D4 D7C8 MOVB 8,*15 09D6 0945 SRL 5,4 09D8 D4C5 MOVB 5,*3 Write auxiliary values 09DA 06C7 SWPB 7 09DC 0947 SRL 7,4 09DE D4C7 MOVB 7,*3 09E0 0228 AI 8,>C002 New address motion table 09E2 C002 09E4 060C DEC 12 Last sprite? 09E6 15C4 JGT >0970 No, once again * end sprite loop 09E8 0A11 SLA 1,1 START of sound processing...
  23. No problem. CAMEL99 does as well. In fact the standard Forth context switcher should be able to be installed as an ISR. You would just have to be careful with tasks that used shared resources. Create some locks or something like that.
  • Create New...