Jump to content

TheBF

+AtariAge Subscriber
  • Content Count

    2,874
  • Joined

  • Last visited

Everything posted by TheBF

  1. As I am testing out my system I am trying to run other peoples Forth Code. This BANNER routine uses a data matrix to define big fonts. All I had to do was add my version of the UPPER routine to convert chars and my BYTES word to compile the data matrix and it worked.
  2. Actually just LWPI >8300 That way you wont't pollute R13, R14 and R15 in the Other workspace. And that only guarantees you 8300 to 830F (16 words of memory for registers) Consult the Editor Assembler manual for the usage of the memory at 8300 to 83FF
  3. Well... the TI system is using the workspace at 83E0 and it kind of assumes it owns them. So if you want a full register set for your program, it's available. And if you don't change you have to save at least some of the registers in order to use them. Also it's simpler to let TI-99 keep it's workspace when you call some of the system calls like KSCAN and such. So the good news is there is a nice space at >8300 that is waiting for your program to use as a workspace. Take it and have 16 registers to yourself. B
  4. I saw some super posts in the archives about sound ripppers and stuff related to sound so I thought I would take a run making a sound list player. All of this can be done in FB Forth or TF as well. However there are some words that will need adjustment. The missing words would be MS, which is just a quick delay loop. : MS 8 0 DO LOOP ; ( something like this, 8 is "about" the correct number for 1 MS) And PAUSE which is a noop. : PAUSE ; will fix that quickly. And ?NUMBER in CAMEL Forth is non-standard, but Lee created an equivalent in FB Forth for another one of my posts a while back. (Will find that and post here) However what a little player like this might be good for is trying out some data to listen to it for ALC programmers. You can feed it sounds lists from the keyboard and play them to see how you like the sound. Couple of things to be aware of: 1. This player turns all 4 sound channels off when it gets to the end of a list with the word SILENT. If you use the TI player you will need to add the string of bytes to turn off your sounds. 2. The BYTES word does not use commas, so sound lists tried here will have to have the commas removed. 3. UN-comment SND! for other Forth systems or use the equivalent word to write a byte to the sound chip address. 4. LSHIFT is the ANS Forth word, but TF uses << for the same thing I believe. Not sure about FB Forth . How it works: PLAY$ takes the address of a string of bytes with a count byte at the start and a time duration byte at the end. It converts the duration byte to milliseconds assuming a 1/60 of a second ISR time. It then computes the start and end address of the sound bytes and loops through them sending each one to SND! (the chip address) When the loop is completed it waits for the duration of milliseconds. PLAY Is the high level word to use for playing a sound list. It simply looks to see if there is a non-zero byte count in a sound string. While that is true is calls PLAY$. Then using the count byte it computes the address of the next string in the list and plays the next string until it hits a final ZERO. CREATE XXXX is Forth's way of making a new word in the Dictionary of words and will return the start of the data address of that new word. BYTES is a compiler directive that was created to parse the input stream, convert the data to a number and compile it into the next byte of memory. It could have been done with the C, primitive but it makes the lists harder to read ( example AA C, 7 C, DE C, ETC... ) I believe the code here will run with the CAMEL99 version in GITHUB /BIN folder. (did not try it yet) I stole some sounds from PARSEC for FIRE and EXPLODE and I must confess it was a religious experience to hear those sounds come out my Forth system. If you get it working type: 3 SHOTS at the console. That does 3 FIRE sounds and then runs the EXPLODE list. Memories...
  5. This points to some deep insights into the architecture of our brains. I don't know what exactly, but the fact that we can't see the parallel lines clearly makes me wonder what else do I misperceive? I suspect that answer is a great deal! And if anybody here sees the parallel lines clearly, they are either a mutant or should maybe see their doctor :-) Thanks Sometimes99er! B
  6. I always like the way Wirth's languages have these different data types. Forth makes you work hard for that nice stuff. So in my Pascal Envy fever I thought I would see what it would take to make a "packed array of bits" and it is more code than I thought. The overhead just to set 1 bit, according to the CLASSIC99 9901 timer is 1.8 milli-seconds in my Forth system. EDIT: New times 1.5mS to set a bit. 1.1mS to read a bit ([email protected]) This could be really sped up with assembly language code words but it's still a lot of work to strip out bits. EDIT: Here is the revised code with fixed RSHIFT and uses CELL+ and CELLS to be less platform dependant. \ BOOLEAN array experiment \ BOOLEAN data is one CELL (16 bits on TMS9900) HEX \ create & erase memory area for 'n' bits : BITS: ( n -- ) CREATE 8 / HERE OVER 0 FILL CELL+ ALLOT ; \ added 2 bytes extra \ compute bit# in a cell & cell address : BITFLD ( bit# addr[] -- bit#' addr) SWAP 10 /MOD CELLS ROT + ; : [email protected] ( bit# addr -- ? ) BITFLD @ \ compute bit# & fetch bits in cell SWAP RSHIFT \ if bit#<>0 RSHIFT, 0001 AND ; \ mask 1 bit : BIT#>MASK ( bit# -- n ) 0001 SWAP LSHIFT ; : BSET ( bit# addr[] -- ) BITFLD ( -- bit# addr) SWAP BIT#>MASK >R \ save the mask DUP @ \ -- addr bits R> OR SWAP ! ; \ or mask into bit, store in addr : BRST ( bit# addr[] -- ) BITFLD ( -- bit# addr) SWAP BIT#>MASK INVERT >R \ invert and save mask DUP @ \ -- addr bits R> AND SWAP ! ; \ mask out bits, store back in addr \ test code DECIMAL 300 BITS: ]X \ make array X of 1000 bits : FILLBITS 300 0 DO I ]X BSET LOOP ; : CLRBITS 300 0 DO I ]X BRST LOOP ; : EVENBITS ." Erasing..." CLRBITS 300 0 DO I ]X BSET 2 +LOOP ; : SHOWBITS 300 0 DO I ]X [email protected] . LOOP ;
  7. I didn't need GPL to move data from GROM to RAM. The GROMs are memory mapped devices. I set the address and then read the GROM data and put it somewhere in RAM. I did it in Forth using the routine that reads a character from a memory location. ([email protected]) Which is written in assembler. Then I can put it anywhere I want to. Am I missing something about GROMS? (very possible) : GROM ( addr -- ) \ set GROM address SPLIT 9C02 C! 9C02 C! ; : [email protected]+ ( -- c) 9800 [email protected] ; \ GROM char fetch, w/auto-incr
  8. It makes me wonder how big a "Boolean" type is in the P-Code system. Any idea? ​Oops re-read your earlier post completely. 16 bits for a Boolean. How big can you go before it hits the limit? Could you try ARRAY of CHAR ?
  9. 3 MHz clock, 14 cycles or more to do almost anything and you get a VERY slow machine. I would estimate 120,000 instructions per second or less. State of the art circa 1970s By comparison the modern MSP430 from TI which has a similar instruction set and register count to the TMS9900 does some instructions in 1 clock cycle but on average takes about 2.5 So with the same 3MHz clock it would give you 1,200,000 instructions per second. This is part of the fun of programming the old devices. You must be smarter <oldfartrant> than these kids today, programming multi-core GHz processors in Javascript.</oldfartrant>
  10. Yes. I did not run your code with 10000 primes
  11. That's great optimization. Thanks telling us about it.
  12. That's a pretty cool trick. You are the font master. I added these simply routines to my system to access GROM without the Assembly language : GROM ( addr -- ) SPLIT 9C02 C! 9C02 C! ; : [email protected]+ ( -- char) 9800 [email protected] ; \ read & auto-increment address : GVMOVE ( grom_addr vdp_addr cnt -- ) \ GROM->VDP move ROT GROM BOUNDS DO [email protected]+ I VC! LOOP ; : BIGCAPS ( -- ) 4B4 20 ]PDT 200 GVMOVE ; Which let me write this demo : TEST GRAPHICS 8 9 AT-XY ." TEXAS INSTRUMENTS" 10 11 AT-XY ." HOME COMPUTER" 2 16 AT-XY ." READY-PRESS ANY KEY TO BEGIN" 4 22 AT-XY (c) ." 1981 TEXAS INSTRUMENTS" 0 0 AT-XY 0 4 DO I . 1000 MS -1 +LOOP 0 0 BL 10 HCHAR BIGCAPS BEGIN KEY? UNTIL BYE ;
  13. Yes this one just finds PRIME numbers. It look like it was done by Anton, but I can't be sure. So if we scale the Starting Forth version to 10000 it would take 25 seconds, so this one is quite a bit faster. With this version in TF it should search to over 20,000 because it uses all available memory in the expansion card.
  14. I was originally working replicating the C version A forgot to remove SQRT from this file. Had to go socialize. Wife's orders B
  15. Yup! And in fact in should be : SIEVE[]@ ( n -- ? ) SIEVE[] + [email protected] ; I merged the address into these new definitions to make it read nicer. B
  16. This is the way most Forths are implemented for both stacks for all the reasons you state. In CAMEL99 I tried a common optimization and CACHE the top of stack value in a register. This changes where you have to push and pop but on balance it speeds up a Forth system by about 10%. Simple Math operations become: A *SP+,R4 versus A *SP+,*SP Fetching the contents of a variable is: MOV *R4,R4 I like it.
  17. So I changed the benchmark to use a VALUE to hold the array address. It improved TF and CAMEL99 ITC version, but it actually slowed down the directed threaded version a little. The DTC version has HERE written in Assembler so this only proves that my code for CONSTANT is slower than fetching the contents of a variable in ASM. Anyway here is result. \ Sieve of Erathosenes in Forth using a VALUE as array pointer \ Tested with TurboForth and CAMEL99 Forth 0 VALUE SIEVE[] \ holds array address \ array calculators : SIEVE[]@ ( n addr -- ? ) SIEVE[] + [email protected] ; : SIEVE[]! ( n addr -- ) SIEVE[] + C! ; : ERASE ( addr n -- ) 0 FILL ; HEX : FREEMEM ( -- n) FF00 HERE - ; : ?MEM ( n -- n ) FREEMEM OVER < ABORT" Out of memory" ; \ byte array uses unallocated memory at HERE DECIMAL : PRIMES ( n -- ) ?MEM HERE TO SIEVE[] \ assign address to the array CR ." Running..." SIEVE[] OVER ERASE 1 0 SIEVE[]! \ mark as prime like 'C' version 1 1 SIEVE[]! 2 \ start at 2 BEGIN 2DUP DUP * > WHILE DUP SIEVE[]@ 0= IF 2DUP DUP * DO 1 I SIEVE[]! DUP +LOOP THEN 1+ REPEAT CR ." Complete." CR DROP ." Primes: " 2 DO I SIEVE[]@ 0= IF I . THEN LOOP ; 10,000 primes W/HERE W/VALUE TF 8.0 7.7 CAMEL99 ITC 10.28 8.4 CAMEL99 DTC 7.25 7.5
  18. I have no doubt about it Rich. Do you have an approximate number of secs? I did not make it clear, but I only time the loop that does the calculation. The print out of the results I ignored. I also did some testing and realized the Forth example is pretty silly in one other way. The HERE word reads an address from a variable and is used as the base address of the data array. So that's not at all like using a fixed memory address for the data array. When I replaced a Forth version of HERE with an Assembly language version of HERE the ITC benchmark improved 20%! "HERE" is a convenient memory location for throw away calculations where you don't need to create a variable, but not appropriate for a speed benchmark IMHO. So I will change that and republish the results for all the Forth examples.
  19. A convention in my experience is to have stacks grow downwards from high RAM and memory usage to grow upwards. Not written in stone somewhere, just something I have seen. BF
  20. I thought I would expand this conversation by using a classic benchmark, the Sieve of Erathosenes. I know it's an old chestnut, but it is commonly used to see how a language does a few things. I tried it first with TI-BASIC. I used the MSX version from Rosetta code and fixed a few things so it would run on the TI-99 100 REM Eratosthenes Sieve 110 REM for TI BASIC 120 REM ****************** 130 REM MSX BRRJPA 140 INPUT "Search until: ":L 150 DIM P(1500) 160 FOR N=2 TO SQR(L+1000) 170 IF P(N)<>0 THEN 210 180 FOR K=N*N TO L STEP N 190 LET P(K)=1 200 NEXT K 210 NEXT N 220 FOR N=2 TO L 230 IF P(N)<>0 THEN 250 240 PRINT N; 250 NEXT N I use 1000 for the input value because it takes along time. The big shock for me was that XB was slower than TI-BASIC. Then of course I did a version for Forth and tested it on Turbo Forth and Camel99 a couple of ways. Here is that code which is also from Rosetta Code but slightly modified by me. I don't agree with the way they did the looping in the WHILE statement. It's a slow thing to have multiplication to calculate a loop I will see if I can make it more like the C version... \ Sieve of Erathosenes in Forth \ Tested with TurboForth and CAMEL99 Forth \ array calculators : []@ ( n addr -- ? ) + [email protected] ; : []! ( n addr -- ) + C! ; : ERASE ( addr n -- ) 0 FILL ; HEX : FREEMEM ( -- n) FF00 HERE - ; : ?MEM ( n -- ) FREEMEM OVER < ABORT" Out of memory" ; \ byte array uses unallocated memory at 'HERE' DECIMAL : PRIMES ( n -- ) ?MEM CR ." Running..." HERE OVER ERASE 1 0 HERE []! \ mark as prime like 'C' version 1 1 HERE []! 2 \ start at 2 BEGIN 2DUP DUP * > WHILE DUP HERE []@ 0= IF 2DUP DUP * DO 1 I HERE []! DUP +LOOP THEN 1+ REPEAT CR ." Complete." CR DROP ." Primes: " 2 DO I HERE []@ 0= IF I . THEN LOOP ; ​And here are the timings. BASIC can't allocate enough memory for more than about 1500 elements because of the big floating point numbers so I limited it to 1000. 1000 primes X BASIC 14.3 secs TI BASIC 11.9 10,000 primes TF 8.0 CAMEL99 ITC 10.28 CAMEL99 DTC 7.25 I would be interested in seeing the GCC results using the Rosetta code version or something similar.
  21. Nice. I am having more trouble that I thought I would have, re-creating the motion characteristics of XB. Ah the programmers life. I guess considering that I am standing on top of a cross-compiler/assembler that I wrote, on top of the Forth kernel that it generated on top of the multitasker, on top of the sprite library I should realize it's a house of cards. :-) I have 2 rules about software: 1. Anything is possible. YEAH! 2. Nothing is easy (oh yeah).
  22. Very cool. Amazing what you can coax XB to do. I will do this one too. For this one I will have to load my sprite library and the multi-tasker. I don't use interrupts for auto-motion in this system. But I think I can do it... B
×
×
  • Create New...