Jump to content

Willsy

Members
  • Content Count

    3,144
  • Joined

  • Last visited

Posts posted by Willsy


  1. 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! :D ;)

     

    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! :D


  2. 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


  3. 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? ;)


  4. 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


  5. 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%)

     

    :D

     

    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. :cool:

    Clearly, assembly can beat this, and GCC too, since GCC churns out assembly.

     

    Mark

    • Like 1

  6. 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


  7. 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! :D :P


  8. >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 :roll: )... something tells me that reading status register #1 will not be as simple as reading >8803 :(


  9. 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

    • Like 1

  10. 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! :D <-- big smiley!


  11. 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! ;)


  12. /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]


  13. 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? ;)


  14. 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?


  15. 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.

×
×
  • Create New...