-
Content Count
3,144 -
Joined
-
Last visited
Posts posted by Willsy
-
-
Excellent job! Well done!
-
I missed this when I was fixing things for the weird byte format.Weird format? WEIRD FORMAT? It might be in your world, mate, but it's perfectly normal for us!
By the way, I think TI chose to store bytes this way to allow the comparison logic to work for word and byte values. This would also remove the need for sign extend and zero extend logic. It also opens up the possibility of storing data in the otherwise unused low byte. I don't have anything to back up this reasoning, but it seems to make sense. In any event, no matter why this decision was made, we're stuck with it now.Well, big endian is actually very common. The TMS9900 may have been one of the first/the first bits of silicon to be big endian, but IIRC all the mini's of the day were big endian. The entire 68K range is big endian etc... Don't forget the TMS9900 was an attempt to consolidate an entire CPU board into a single chip, so the big-endian constraint was already established. Anyway, sorry to hijack... I'm getting away from the GCC compiler... I'll shut up now!

-
That's lovely! Watching it now, thanks!
-
Can we have a jumper to change it? Pretty please?
I mean, you're not using a real '379, right? The DIL ones are obsolete as far as I know. Presumably the '379 is emulated in the fpga? Sorry if I've got this all arse about face... I'm ill in bed so I'm hogging the forum!
-
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
-
Why use the inverted outputs of the '379? Especially as there is programmable logic already on the chip. It makes coding a f**ing pain in the ass, quite frankly.


-
That looks correct, it would return an int. It may not be what you intended, but I think the compiler got it right...?
-
The keyboard type is set by writing to the byte >8374.
TurboForth writes the value >05 into >8374 to enable a 'full' keyboard scan in both upper and lower case.
More info here: http://nouspikel.group.shef.ac.uk//ti99/tutor1.htm#KSCAN
-
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
-
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?

-
Feature request (pretty please)!
A routine to time sections of code. How would we integrate something like that into the Classic99 TI-99/4A "architecture"?
How about:
Write to CPU ram >0000 (any value) - zero's timer and begins counting
Write to CPU ram >0002 (any value) - stops counting and places the accrued number of milliseconds at >FFFA
So:
CLR @0 ; begin timing
LI R0,0
LI R1,BUFFER
LI R2,768
BLWP @VMBR
CLR @2 ; stop timing
Number of milliseconds to be found at >FFFA (and possibly in the debug window
)Whaddaya think?
Mark
-
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 [email protected] 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 [email protected] 1 OR SWAP V! \ lower brick in row RND 23 MOD 5 << RND 31 MOD + DUP DUP [email protected] 2 OR SWAP V! 1+ DUP [email protected] 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
- Initialise random number seed
-
Very nice looking! I can't believe you did all that in that crazy polish notation of Forth!
It's easy when you get used to it! It's the simplicity of it (when the penny finally drops!) that will blow your mind!
Mark
-
Any idea when you might get back to working on this?
I can't say I don't have enough free time, I broke my shoulder three weeks ago on bicycle.
Pah! You have TWO shoulders. That means you still have one spare shoulder! GET TO WORK!

-
I'm not sure if Lucien posted this to AA or not... I certainly didn't see it...
http://www.youtube.com/watch?v=7EtfsyyBdXA
Really nice! Is it ready to release and play?

-
>8802 Read the status byte from the status register, resets it, and clear interrupt.
Hmmm.... Ok, so, it's memory mapped... (duh! what else could it be
)... something tells me that reading status register #1 will not be as simple as reading >8803 
-
Er, my mind has gone blank. How do I read the status register on a standard 9918? More to the point, how do I read status register *1* on a 9938? Has anyone got any ideas?
Mark
-
Thanks. Excellent video. GPL is apparently not worth it, when C is so much easier.
Wow! Yes! Awesome! That beats TF, for sure. However, the development cycle in TF is much nicer

Okay, it looks like GCC has set the bar that I have to try to match/beat. This may take some time!
Regarding GPL, I think if you can write TMS9900 assembly, you'll have no problems learning GPL. Personally, I'm very interested in learning it - I won't feel like I've fully explored the TI and got the TI coder black belt until I do!
Mark
-
1
-
-
I feel like I am in some kind of battle with you. Am I correct?
Not at all - The TI is my hobby, and I don't spend/waste negative energy on my hobby! Hobbies should be fun! This isn't an argument (at least, not for me) - it's merely a discussion. We might both learn new stuff along the way. That's what it's all about!
<-- big smiley! -
Sure I have been using GPL for sometime now, more than most others. I convert from Basic, XB and Assembly to GPL. Like Assembly I can use a address instead of a variable for storage.
That's just semantics! A variable *is* a memory location! Where do you think TI BASIC or XB (or Forth) programs store their variables? They are stored in memory, of course!

-
/CS = PIN 9 ; chip select will be on pin 9, true will be a low
/CS = (A1 + A2 + A3) * MEMEN ; /CS=(a1 OR a2 OR a3) AND MEMEN
[pedantry]
Actually, I don't *think* it was clever. If you wanted active low, then you would do something like:
CS = PIN 9 ;
CS = NOT((A1 + A2 + A3) * MEMEN) ;
But you get the idea

[/pedantry]
-
Looking at Marcs logic diagrams, could the discretes not all be mopped up into a single programmable chip? Back when I was active in such things, we had chips called PALs and GALs - these were tiny, say, 12 or 16 pin DIL chips and you could program (and re-program) them with your own logic. We used a neat little program called PALASM to program the chips. You just used boolean logic equations. It was very simple and did the job perfectly.
/CS = PIN 9 ; chip select will be on pin 9, true will be a low
/CS = (A1 + A2 + A3) * MEMEN ; /CS=(a1 OR a2 OR a3) AND MEMEN
Something like that. Does anything with this level of "simplicity" still exist? Or are they all massive big hairy FPGAs for building your own floating point processor?

-
On the cartridge case front, I now have a set of case molds. I just have to set up a compressor and pressure pot to use them to make new cases. Black will be easiest to do, but I may get into colors later, as that does require some post curing of the plastic. Setting this up is one of the items on my near-term to-do list.
I personally would *love* translucent cases. Not transparent, but translucent! I had the idea of having two LEDs in the TF cart, one red, one green, and giving the user access to these:
1 GLED ( turn green led on)
0 GLED ( turn it off)
1 RLED ( turn red led on)
0 RLED ( turn it off)
If the user generated an error when typing in code, the red led would turn on, etc, so the whole cart would glow red!
Love it!
Can we have LEDs in the design please? PLEASE?
-
Okay, for me, 1K in the upper cart memory address would be just fine. Would it be possible to include a jumper for 4K or 1K RAM?
(You know, since we're just kicking the tyres here!)
Yes It would be possible. Would require some more decoding but would work.
The reason I designed it this way though was to have not only RAM and banked ROM but also a large area that was always apparent. Ideal ,in my opinion, to run a small memory manager out of (among other things.) This allows a programmer to store subroutines in banks. When a subroutine is called then that page can be swapped in and the BL made from RAM. When the sub is over then the return is made to RAM where the original calling page is brought back to life and PC restored. It's kind of like a BLWP except you have a page vector and a branch vector
Example....
Say page 1 wants to call a routine in page 16.....
From ROM.....
Store existing ROM page# in RAM (1)
Load New ROM page# in RAM (16)
Load branch target in RAM (yyyy)
BL to paging routine in RAM
Store R11
switch banks MOVB >0010,@ROM
BL to target address yyyy
when routine is over.....
B to ram unbanking routine
Restore ROM bank MOVB >0001,@ROM
restore R11
RT
This would not be a large amount of code for sure but the rest of RAM could be used for common routines or data storage or whatever gets you going. Since it is always on the buss so to speak then it is available from all ROM pages.
Since the cart always starts in bank 1 (hopefully) then that bank would simply download itself to the RAM and go from there.
Having never done anything that works from bank switched ROM I can't speak to the advantages or disadvantages to this scheme so you will have to chime in with your thoughts.
Marc, the more I think about your suggestion, the more I like it. It's kind of the opposite way around to how I've been doing it, but I like it.
In TF there are two 8K banks:
Bank 0: Holds the entire Forth dictionary, all words written in Forth (including the interpreter compiler), and common words written in assembly that must run quickly (I.e. I don't want to 'spend' on the time required to do a bank switch), such as all the stack manipulation words and math words.
Other words (e.g. sprites etc) have their 'stubs' (i.e their Forth dictionary entries) in Bank 0, but the code just does a:
BL @BANK1 DATA _SPRITE
and runs the (assembly) code in bank 1.
There is a "bridge" routine in pad ram (i.e. the routine BANK1, above) is in pad ram that actually pages in bank 1, and does a branch to the intended location (_SPRITE in the example above).
The code in bank1 calls another routine in pad to page bank 0 back in again, and resume. Both routines are very small.
However, I like the idea of just copying a routine in 4K of memory from a bank, and executing it from there. Like you say, commonly used stuff would be copied in at startup and stay there throughout the life of the application. Neat.

Walking through bank switching
in TI-99/4A Development
Posted
That's great news. This really is going to be a great board! I already have plans for a major project with it... Sigh...!
Thanks to you all.
Mark