Jump to content

TheBF

+AtariAge Subscriber
  • Content Count

    3,166
  • Joined

  • Last visited

Posts posted by TheBF


  1. 1 hour ago, apersson850 said:

    Yes, since the TMS 9900 doesn't have autoincrement deferred, like Digital's VAX, that's the best you can do.

    For those unfamiliar with the architecture, the VAX 11 from Digital was a 32 bit architecture, with an ortoghonal instruction set and plenty of addressing modes. Where the TI in its general addressing mode has a two bit addressing mode specifier, the VAX 11 had four bits. Thus a large number of addressing modes was possible. Autoincrement implied indirect, but you could also do autoincrement deferred, which meant that the register pointed not to the data, but to the address of the data. Double indirection, in other words.

    I have never used it but I understand that the 6809 could do DTC NEXT in one instruction as well. 

     

    There is lot of stuff I don't understand but when I looked at the RISC 5 instruction set it seems extremely verbose to do simple things.

    I get the RISC idea but the choices made for what the instructions should be seem odd to me.

     

    And... nobody seems to care about sub-routine calling overhead in these new designs. I suppose because memory is so cheap. Just put code inline.

    Chuck Moore built machines that did sub-routine calls in 1 cycle and return was just bit 15 that could be set on your instruction so it was free. 

    • Like 2

  2. 4 hours ago, Willsy said:

    Nice. I think TF does something similar? From memory, TF puts the address to write the value stack in line with a word called doTO. 

     

    10 TO FRED

     

    Might nievely compile to:

     

    LIT 10 LIT FRED !

     

    TF compiles it as LIT 10 doTo FRED

     

    One less run through the inner interpreter. 

    Sounds the same yes. In my case Store is slower when you cache TOS in a register so I really win using the extra register instead of the stack.

    But in the end it really is about removing cycles through the inner interpreter if you want to go faster.

    I have broken my DTC version somewhere in the branching mechanism.  I don't think DTC is very practical for TI-99 because of size, but it it is certainly fun to watch it run with a two instruction NEXT.

     

    *IP+ W MOV,

           *W B,

     

    And the way I did it R11 becomes the W register which is pretty neat.

     

    I started reviewing something I call ASM Forth that I started but didn't finish. When I can see that it works well enough, I will put it up on Github. We might have to start another topic for that one. :)

     

    • Like 4

  3. I am always amazed how a small change can make a difference.

    So I was testing my latest kernel build and I found the Benchie benchmark.

     

    It assigns a VALUE in the middle of the loop.

    I was getting a timing of 26.25 seconds  whereas TurboForth could rip this off in 24.5 seconds. 

    5 CONSTANT FIVE
    0 VALUE BVAR
    HEX
    100 CONSTANT MASK
    
    : BENCHIE
             MASK 0
             DO
                1
                BEGIN
                  DUP SWAP DUP ROT DROP 1 AND
                  IF FIVE +
                  ELSE 1-
                  THEN TO BVAR
                  BVAR DUP MASK AND
                UNTIL
                DROP
             LOOP ; 

    I wondered if assigning that VALUE with TO was the problem.  My TO  code just used LITERAL and !. Just bog standard Forth.

    Seemed like a long shot to me but what the heck.

     

    So I did this and created a literal operator that did the store and removes 2 instructions to push the stack down by using R1 for the address.

    CODE LIT!  ( n addr -- ) \ combine function of LIT and !
               *IP+  R1 MOV,
                TOS  R1 ** MOV,
                     TOS POP,
               NEXT,
               ENDCODE
     .( .)
    : VALUE   CONSTANT ;
    
    : TO  ( n -- )
               ' >BODY   \ compute PFA at compile time
               STATE @
               IF  POSTPONE LIT!  ,  EXIT
               THEN  ! ; IMMEDIATE
    

    And... just like that it's 24.5 seconds. 

     

    • Like 4

  4. 3 hours ago, Bill R Sullivan said:

    This reply is mainly to Lee and "The BF", as their replies have made me realize just how much I would have to do to make all my established methods work in fbForth.  Plus the fact that I will also be using these methods for my SGCPU in my SNUG TI-99/4P system which has a 16 bit 1MB SAMS!  My 80 year old brain can't deal with this change no matter how much help you guys give me.  I even considered just retiring from my TI hobby, but then considering all the time I spend in my now tiny "Man Cave" due to COVID19 lock downs I would surely end up in the psycho ward for the duration of my life.  I can only apologize for taking up your time and diverting the mainline conversation for my sake.  I will just go to my corner and think only X4th99, as it is only intended to keep my mind functional during these hectic times of the Corona Virus and it's mutations!

     

    Extremely crazy old guy,

    RetroBill (FDOS)

    I sure get the challenge of fitting Forth code from one system into another. It ain't easy.

    No need to apologize. We all are doing the same thing here. Just helping each other along.

    But please carry on with your Forth and if there is anytime you say "I wonder if the other guys have an idea for this",  Lee and I enjoy that kind of discussion.

     

    Keep the Forth :) 


  5. 2 hours ago, Bill R Sullivan said:

    Lee, truly amazing!  Maybe I should consider dropping X4th99 development and just use fbForth, provided I can easily learn how to use your memory manager!

     

    RetroBill (FDOS)

     

    I have some useful structures too Bill that could be bolted onto FbForth.

     

    On is like BLOCK but it's SAMS blocks. 

    SBLOCK ( n -- addr)  

    Pick the SAMS page and it pulls it in and returns the address where it lives.

    An SBLOCK is 4K.  There are 2 alternating buffers that live in a consecutive 8K space.

    You can put them where you what in memory with one constant in the code. 

    But with 2 buffers you can do SAMS to SAMS transfers easier.

    This is in Forth assembler so it would need a few tweaks for FbForth.

    But it could also be made with >MAP in Forth and it would still be way faster than BLOCK. 

     

    And then there is PAGED ( virtual-addr -- real-addr) 

    You set a 64K segment of SAMS memory with:  HEX 10 SEGMENT 

    Now you have a 64k address by adding PAGED to your memory operations.

    : @S    ( addr -- n )  PAGE @ ;
    : !S    ( n addr -- )    PAGE ! ; 
    etc...

     

    PAGED would not be hard to port to FbForth I think. Lee and I just have not tried. :)

    So lots of possibilities with the gang here. 

     

    Spoiler
    \ SAMS CARD support for CAMEL99 in Forth   May 2020  B Fox
    \ 16 by 64K segmented memory model for DATA
    \ You can access any ADDRESS from 1 to 65535 using PAGED
    NEEDS SAMSINI  FROM DSK1.SAMSINI
    
    HERE
    VARIABLE SEG     \ holds current 64K segment
    VARIABLE BANK#   \ current mapped bank
    HEX
    3000 CONSTANT DMEM    \ CPU RAM memory block location
    \ Legal values: 2000,3000,A000,B000,C000,D000,E000,F000
    
    \ compute SAMS register based on PMEM address
    DMEM 0B RSHIFT 4000 + CONSTANT DREG
    
    HEX
    : PAGED  ( virtual-addr -- real-addr)
          SEG @ 1000 UM/MOD  ( -- offset bank#)
          DUP BANK# @ -     \ different page?
          IF
            DUP BANK# !
            SAMSCARD 0SBO   \ card on, enable registers
    ( bank#) >< DREG !      \ swap bytes & store in SAMS register
             0SBZ           \ card off
          ELSE DROP
          THEN DMEM +       \ then add offset to paged mem block
    ;
    \ safely set the 64K segment that you want to use
    : SEGMENT ( 1..F -- ) \ don't allow segment 0
          DUP 01 10 WITHIN 0= ABORT" SAMS segment err"
          SEG ! ;
    
    CR HERE SWAP - DECIMAL . .( bytes)
    SAMS-OFF SAMSINI SAMS-ON
    1 SEGMENT
    CR .( SAMS card activated)
    CR .( Window = ) DMEM HEX U.
    CR .( Segment = ) SEG @  DECIMAL U.
    CR
    

     

     

    • Like 4

  6. In my obsessive way I took this game and used it to learn how to make my system work better.

    I never wrote games so it gave me a good skeleton and poked holes in my system. 

    It is also politically incorrect since my snake likes to eat mice not apples. ;)  oops! 

     

    Anyway here is the source code after 6 interations and blowing up my underlying code. :) 

    I must confess that it does get a bit addictive trying to go at "Viper" speed. 

    It doesn't have a remembered scoring system so it's still not a finished product. 

    I attached an executable program for the E/A cartridge option 5.

     

    Spoiler
    \ snake  a simple game in Forth ported to CAMEL99 Forth
    \ DERIVED FROM: https://skilldrick.github.io/easyforth/#snake
    \ revised to use CAMEL99/TI-99 features
    CR .(   \\\\\\ Version 6 \\\\\\\\ )
    \   \\  snake sounds and mouse squeak  \\\\\
    
    INCLUDE DSK1.RANDOM
    INCLUDE DSK1.GRAFIX
    INCLUDE DSK1.CASE
    INCLUDE DSK1.ARRAYS
    INCLUDE DSK1.UDOTR
    INCLUDE DSK1.MARKER
    
    CR .( compiling Snake...)
    \ =======================================
    \ We use direct control of the sound chip
    \ rather than sound lists and a player.
    HEX
    \ noise control words
    : NOISE   ( n -- ) E0 OR SND! ; \ n selects the noise type
    
    \ noise envelope control
    : NOISE-DB   ( db --) F MIN F0 OR SND! ;
    : NOISE-OFF  ( -- )   F NOISE-DB ;
    
    DECIMAL
    : NOISE-UP ( speed  -- )
              2 15 DO  I NOISE-DB  DUP MS  -1 +LOOP DROP ;
    
    : NOISE-DOWN ( speed -- )
             15  2 DO  I NOISE-DB  DUP MS    LOOP DROP NOISE-OFF ;
    
    \ channel 1 sound control words
    DECIMAL
    : f(clk) ( -- d) 46324 1  ;   \ this is 111,860 as 32 bit int.
    
    \ >FCODE re-arranges freq. value nibbles (4bits) for the TMS9919
    HEX
    : >FCODE   ( 0abc -- 0cab)    \ ASM would make this much faster
              DUP 0F AND SWAP      ( -- 000c 0abc)
              4 RSHIFT             ( -- 000c 00ab)
              SWAP ><  ( SWPB)     ( -- 00ab 0c00)
              + ;
    
    : HZ>CODE  ( freq -- fcode )  f(clk) ROT UM/MOD NIP >FCODE 8000 OR  ;
    
    \ *TRICKY STUFF*
    \ Calculating the 9919 freq. code takes too long BUT we can convert frequency
    \ to 9919 chip code at compile time then compile as 16 bit literal number
    \ using this text MACRO
    : [HZ] ( freq -- fcode ) S" HZ>CODE ] LITERAL" EVALUATE ;
    
    \ sound channel #1 control words
    : FREQ!    ( fcode -- ) SPLIT SND! SND! ;
    : ]HZ      ( freq -- ) [HZ] POSTPONE FREQ! ;      \ pre-compiled fcode version
    : HZ       ( freq -- )  HZ>CODE SPLIT SND! SND! ; \ runtime calculation version
    : DB       ( n -- )    90 OR SND! ;
    : MUTE     ( -- )      9F SND! ;
    
    DECIMAL
    500 CONSTANT MAXLENGTH
    
    \ x/y coordinate storage for the snake
    MAXLENGTH 2+ ARRAY ]SNAKE-X
    MAXLENGTH 2+ ARRAY ]SNAKE-Y
    
    : SNAKE-X-HEAD  ( -- addr)  [ 0 ]SNAKE-X ] LITERAL ;
    : SNAKE-Y-HEAD  ( -- addr)  [ 0 ]SNAKE-Y ] LITERAL ;
    
    .( .)
    VARIABLE SPEED
    VARIABLE PREY-X
    VARIABLE PREY-Y
    VARIABLE DIRECTION
    VARIABLE LENGTH
    
    \ characters used
    DECIMAL
    136 CONSTANT SHEAD  \ use different color set
    42  CONSTANT SNAKE
    128 CONSTANT PREY
    30  CONSTANT BRICK
    BL  CONSTANT WHITE
    
    \ Direction #s
    0 CONSTANT LEFT
    1 CONSTANT UP
    2 CONSTANT RIGHT
    3 CONSTANT DOWN
    
    HEX
    CREATE HEADLEFT  0C16 , 37FF , FF37 , 160C ,
    CREATE HEADUP    1818 , 3C7E , 99FF , 7E3C ,
    CREATE HEADRIGHT 3068 , ECFF , FFEC , 6830 ,
    CREATE HEADDOWN  3C7E , FF99 , 7E3C , 1818 ,
    
    \ array of head patterns
    CREATE HEADS ( n -- addr ) HEADLEFT , HEADUP , HEADRIGHT , HEADDOWN ,
    
    \ set head pattern n to snake's head
    : ]HEADPATTERN ( n -- addr) CELLS HEADS + @  SHEAD CHARDEF ;
    
    : FACE  ( direction# -- ) DUP ]HEADPATTERN   DIRECTION ! ;
    
    \ shape data for PREY, brick, mouse and snake chars
    HEX
    CREATE CLAY   007E , 6A56 , 6A56 , 7E00 ,
    CREATE VIPER  3C5E , EBF7 , EBDD , 7E3C ,
    CREATE MOUSE  0004 , 3E7B , 7FFC , 8270 ,
    CREATE MOUSE2 0008 , 3F7B , 7EFC , 8270 ,  \ mouse looking up
    CREATE JUMPMS 84BE , FB7F , 3C42 , 0000 ,
    
    DECIMAL
    \ get random x or y position within playable area
    : RANDOM-X ( -- n ) C/[email protected]  2-  RND 1+ ;
    : RANDOM-Y ( -- n ) L/SCR 2-  RND 1+ ;
    
    \ text macros make drawing faster.
    : DRAW-WHITE ( x y -- ) S" >VPOS  BL   SWAP VC! " EVALUATE ; IMMEDIATE
    : DRAW-SNAKE ( X Y -- ) S" >VPOS SNAKE SWAP VC! " EVALUATE ; IMMEDIATE
    : DRAW-HEAD  ( x y -- ) S" >VPOS SHEAD SWAP VC! " EVALUATE ; IMMEDIATE
    
    : DRAW-PREY ( -- ) PREY  PREY-X @ PREY-Y @ >VPOS VC!  ;
    .( .)
    : DRAW-WALLS
         0  0 BRICK 31 HCHAR
         0  1 BRICK 22 VCHAR
        31  0 BRICK 24 VCHAR
         0 23 BRICK 31 HCHAR ;
    
    : DRAW-SNAKE
        SNAKE-X-HEAD @  SNAKE-Y-HEAD @ DRAW-HEAD
        LENGTH @ 1
        DO
           I ]SNAKE-X @   I ]SNAKE-Y @ DRAW-SNAKE
        LOOP
        LENGTH @ DUP ]SNAKE-X @  SWAP ]SNAKE-Y @  DRAW-WHITE ;
    
    : INITIALIZE-SNAKE
         6 DUP LENGTH !
         1+ 0
         DO
            12 I - I ]SNAKE-X !
            12 I     ]SNAKE-Y !
         LOOP
         RIGHT FACE  ;
    
    : PLACE-PREY ( y x -- ) PREY-X ! PREY-Y ! ;
    
    : MOVE-SNAKE-HEAD ( n -- )
        DIRECTION @
        CASE
          LEFT  OF  -1 SNAKE-X-HEAD +!  ENDOF
          UP    OF  -1 SNAKE-Y-HEAD +!  ENDOF
          RIGHT OF   1 SNAKE-X-HEAD +!  ENDOF
          DOWN  OF   1 SNAKE-Y-HEAD +!  ENDOF
        ENDCASE ;
    
    \ move each segment of the snake forward by one
    DECIMAL
    \ : MOVE-SNAKE-TAIL
    \    0 LENGTH @
    \    DO
    \       I ]SNAKE-X DUP @ SWAP CELL+ !
    \       I ]SNAKE-Y DUP @ SWAP CELL+ !
    \    -1 +LOOP ;
    
    : MOVE-SNAKE-TAIL
    \      src addr    dest addr      size
    \     ---------    --------- -------------
        SNAKE-X-HEAD  DUP CELL+  LENGTH @ CELLS  CMOVE>
        SNAKE-Y-HEAD  DUP CELL+  LENGTH @ CELLS  CMOVE>
     ;
    
    : MOVE-SNAKE  (  -- )
                 MOUSE2 PREY CHARDEF
                 4 NOISE  8 NOISE-DB   \ soft white noise
                 MOVE-SNAKE-TAIL
                 10 NOISE-DB            \ ramp down noise
                 MOVE-SNAKE-HEAD
                 12 NOISE-DB            \ ramp down noise
                 MOUSE PREY CHARDEF
                 NOISE-OFF ;
    .( .)
    DECIMAL
    : HORIZONTAL? ( -- ?) DIRECTION @ DUP  LEFT = SWAP RIGHT = OR ;
    : VERTICAL?   ( -- ?) DIRECTION @ DUP    UP = SWAP  DOWN = OR ;
    
    : TURN-UP        HORIZONTAL? IF UP    FACE  THEN ;
    : TURN-LEFT      VERTICAL?   IF LEFT  FACE  THEN ;
    : TURN-DOWN      HORIZONTAL? IF DOWN  FACE  THEN ;
    : TURN-RIGHT     VERTICAL?   IF RIGHT FACE  THEN ;
    
    ( EXIT THEN gets out of the case statement faster than ENDOF)
    : CHANGE-DIRECTION ( key -- )
         CASE
           [CHAR] S OF TURN-LEFT  EXIT THEN \ ENDOF
           [CHAR] E OF TURN-UP    EXIT THEN \ ENDOF
           [CHAR] D OF TURN-RIGHT EXIT THEN \ ENDOF
           [CHAR] X OF TURN-DOWN  EXIT THEN \ ENDOF
        ENDCASE
    ;
    
    DECIMAL
    : SWOOSH   ( -- )
               NOISE-OFF
               5 NOISE
               8 NOISE-UP
               20 NOISE-DOWN ;
    
    : NEW-PREY
        SWOOSH
        PREY-X @ PREY-Y @ DRAW-WHITE
        RANDOM-Y RANDOM-X PLACE-PREY
        DRAW-PREY ;
    
    : GROW-SNAKE  ( -- ) 1 LENGTH +! ;
    
    : DEAD-SNAKE  ( -- )
                 NOISE-OFF
                 SNAKE SET#  DUP 11 1 COLOR  250 MS   2 1 COLOR
                 SHEAD SET#  2 1 COLOR  ;
    
    : HAPPY-SNAKE ( -- )
                 [ SNAKE SET# ] LITERAL
                 17 3
                 DO
                    DUP I 1 COLOR
                    I 100 * HZ 0 DB
                    40 MS
                LOOP
                MUTE
                ( -- 5)  13 1 COLOR ;
    .( .)
    DECIMAL
    : DECAY       ( n -- ) 16 0 DO  I DB  DUP MS LOOP DROP ;
    
    : SQUEAK      ( -- )
                  NOISE-OFF
                 [ 3800 ]HZ 0 DB  45 MS  \ pre-computed freq. is faster
                   6 DB  25 MS
                 [ 3500 ]HZ 75 MS
                   8 DB 25 MS
                 [ 1300 ]HZ
                   11 DB 25 MS
                 [ 800 ]HZ
                  MUTE ;
    DECIMAL
    : SCARED-PREY ( -- )
                 JUMPMS PREY CHARDEF
                 SQUEAK
                [ PREY SET# ] LITERAL  DUP  9 1 COLOR
                 2 1 COLOR
                 MOUSE PREY CHARDEF ;
    
    : FASTER    ( -- ) SPEED @ 5 -  1 MAX SPEED ! ;
    
    : CHECK-PREY  ( -- )
        SNAKE-X-HEAD @ PREY-X @ =
        SNAKE-Y-HEAD @ PREY-Y @ =  AND
        IF
           SCARED-PREY
           HAPPY-SNAKE
           GROW-SNAKE
           FASTER
           NEW-PREY
        THEN ;
    
    : COLLISION? ( -- ? )
        SNAKE-X-HEAD @ SNAKE-Y-HEAD @ >VPOS [email protected]
        DUP  BRICK =
        SWAP SNAKE = OR  ;
    
    \ utility words for menus
    : WAIT-KEY   BEGIN KEY? UNTIL ;
    : AT"        POSTPONE AT-XY  POSTPONE ." ;  IMMEDIATE
    
    : INITIALIZE
        PAGE
        4 SCREEN
        MOUSE PREY  CHARDEF
        PREY SET#    2 1 COLOR
        CLAY  BRICK CHARDEF
        BRICK SET#   9 1 COLOR
        VIPER SNAKE CHARDEF
        SNAKE SET#  13 1 COLOR
        SHEAD SET#  7 1 COLOR
        DRAW-WALLS
        INITIALIZE-SNAKE
        RANDOM-Y RANDOM-X PLACE-PREY
    ;
    
    .( .)
    HEX
    : PLAY ( -- )
          BEGIN
             SPEED @ MS
             DRAW-SNAKE
             DRAW-PREY
             83C8 OFF   \ set continuous key reading
             KEY?  CHANGE-DIRECTION
             MOVE-SNAKE
             CHECK-PREY
             COLLISION?
          UNTIL
          0C SCREEN
          HONK
          0B 5 AT" GAME OVER"
          HONK
          DEAD-SNAKE ;
    
    DECIMAL
    : ENDGAME
      5 14 AT" You sure? (Y/N)"
      KEY [CHAR] Y =
      IF    PAGE ABORT
      ELSE  5 14 >VPOS 26 BL VFILL
      THEN
    ;
    
    : SETLEVEL     ( n --) SPEED ! ;
    : BREAK;    POSTPONE EXIT POSTPONE THEN ; IMMEDIATE
    
    DECIMAL
    : MENU
         PAGE
         5 5 AT" Select Start Level"
         5 8 AT" 1 - SNAIL"
         5 9 AT" 2 - WORM"
        5 10 AT" 3 - SNAKE
        5 11 AT" 4 - VIPER"
        5 12 AT" 5 - Quit
        3 23 AT" (It goes faster as you win)"
    
        BEGIN
          5 13 AT-XY
          KEY
          CASE
              [CHAR] 1 OF 150 SETLEVEL BREAK;
              [CHAR] 2 OF 110 SETLEVEL BREAK;
              [CHAR] 3 OF  75 SETLEVEL BREAK;
              [CHAR] 4 OF  50 SETLEVEL BREAK;
              [CHAR] 5 OF  ENDGAME     ENDOF
                            HONK
          ENDCASE
        AGAIN ;
    
    DECIMAL
    : TITLE  ( -- )
          GRAPHICS
          5  5 AT" THE SNAKE"
          5  7 AT" Use the E,S,D,X keys"
          5  8 AT" to move the snake
          5  9 AT" and catch the mouse."
          5 12 AT" The more he eats,
          5 13 AT" the faster he goes!"
          5 20 AT" Press any key to begin..."
          WAIT-KEY ;
    .( .)
    : RUN ( -- )
         TITLE
         BEGIN
            MENU
            INITIALIZE
            PLAY
            500 MS KEY? DROP ( wait for key release)
            2 13 AT" Your snake was " LENGTH @ . ." Ft. long"
            2 15 AT" Press ENTER to play again"
            KEY 13 <>
         UNTIL
         NOISE-OFF
         8 20 AT" SSSSSee you later!"
         1500 MS
         GRAPHICS ;
    
    : COLD   WARM  RUN  BYE ;
    
    LOCK
    INCLUDE DSK1.SAVESYS
    ' COLD SAVESYS DSK3.SNAKE6
    

     

     

    SNAKE6.zip

    • Like 3
    • Thanks 3

  7. 43 minutes ago, GDMike said:

    My brain has to capture this meaning, after I wake up a bit.. bwhhaha...ok...thx lee for this.

    You're both absolutely correct. I need to figure this out.

    Some might say the stack diagram tells you the "gazintas and gazouttas" :)  of a word.

    ( what 'goes into" and what "goes out of")

    • Like 1

  8. This is an interesting problem.

     

    I started playing with sorting the directory of multiple disks.

    I used my Shell program as a start since it had the fundamentals already built.

     

    The method is to read the file names into VDP RAM as a byte-counted compact list.

    Starting at >1000 there is room for 947, 10 character filenames stored this way.

    More room if the file names are smaller.

    This could be modified to be record based and save out to disk.

     

    To sort that array the string addresses are transferred into low RAM.

    So that gives us a potential for 4K pointers 

     

    The sort does a VDP string compare (the strings never leave VDP RAM in this example :) ) 

    It's COMBSORT in Forth, so it's not overly fast. 

    The result of the string comparison controls if the pointer array elements are swapped.

     

    The video shows me using the programmer API commands.

    They can be humanized later.

     

    Time for bed. 

     

     

     

     

     

     

    • Like 2

  9. 10 hours ago, apersson850 said:

    I once wrote, or at least attempted to write, a disk catalog program, which should be able to handle multiple floppies. Those were the only interesting storage media at that time. Here's a list of required functions:

    • Should be able to read the file catalog on multiple diskettes.
    • Files should be possible to list in alphabetical order, even if they were found on different diskettes.
    • The list should include the name of the diskettes they were found on, and/or a reference number for the diskette.
    • The reference number should remain the same for a disk, even if the name was changed.
    • You were supposed to mark the label on the disk with the reference number, but the number was also stored in an unused part of the diskette catalog sector.
    • If you read the same diskette once again, it was recognized from the number stored in the catalog sector. In such a case, the content for that disk was updated, not just read again completely.
    • The list of files should be kept in a data file.
    • It should be possible to start the catalog program at a later time, i.e. not the same day the diskettes were read, and still locate a file name, and the corresponding diskette name/number, by using the stored data from previous sessions.
    • A diskette number should be possible to remove manually from the database, e.g. if the diskette was destroyed.
    • The program required at least two diskette stations, one for the program and the data file with diskette names, the other for reading new diskettes.
    • If the database was large, it would require the program on one drive, the database on a second and new diskettes in a third drive.

     

    Although my basic idea worked, so that I could generate lists of files, with reference to the diskette they resided on, I found that it wasn't feasible when the number of diskettes grew large. 10, 20, maybe even 30 diskettes, well, that kind of worked. But when they grew to a hundred or more, the program became too slow to be reasonable to use. Back then, I had only the standard TI 99/4A, with 32 K memory expansion. I had neither a RAMdisk nor any other additional RAM. Handling the database simply got too slow.

     

    I've been searching here for a while, but can't find anything similar. Has it ever been created? Does a working program of this kind exist? I'm talking about something that can run on a real TI, since emulated versions present other possibilities for overviewing the emulated diskettes.

    Are all the devices starting with DSK1. DSK2. DSK3 etc. ?

     

    Based on our work before and using VDP to hold the strings I might be able to make something like this with the tools I have at the moment.

     


  10. 56 minutes ago, Willsy said:

    I've entered it into TF and it compiles successfully, but how do I run it? When I execute RKEY it just leaves 13 on the stack (enter key). Any example calling code?

    : TEST   BEGIN  RKEY DUP  EMIT  90 = UNTIL ;

     

    Is what I do.

    • Like 4

  11. 17 hours ago, apersson850 said:

    As a stock feature, the p-system has a 32-bit timer that's running all the time, driven by the VDP interrupt. It does of course stop each time you access a floppy disk or similar. It can be ccessed by the intrinsic Pascal procedure time.

     

    My real-time clock has nothing to do with that timer, though. It's implemented with a National Semiconductor MM58167A RTC chip on a card in the PEB. It's visible in some of the pictures in this album.

     

     

    That's is a great looking TI-99.  Thanks for sharing.

    I guess if I really wanted a real time clock I should make a board for my PEB.

    One day maybe. Still lots of S/W ideas to explore.

     

     

    • Like 2

  12. This gives me even more admiration for Apple's Rosetta emulation.

    They seem to be able to emulate Intel on ARM with at most a 2X slowdown.

    And they did it way back emulating Power PC on Intel.

    Makes me wonder how.

     

    I know the 9900 is not like normal machines but it seems like it is a real challenge for ARM to emulate based on your results.

     

    I wonder if Apple is using something like a huge jump table (because they have the RAM) so that instructions compute the address of the emulation code. ??

    Way above my pay grade. :)

     

    • Like 2

  13. 59 minutes ago, GDMike said:

    Ahh. Yes. The "Begin" is what I was missing in my code, I was trying to do everything within a Do LOOP.

    DO LOOPS are great for simple things.

    They can reach out and byte you if you try and use the return stack for some temporary storage because they typically keep the looping information over there. 

     

    So BEGIN    0= UNTIL is a common loop. 0= of course can be any conditional you need. 

     

    Forth's WHILE  loop is unlike any other language because it is split into two sections and you and use them any way you need to.

     

    BEGIN

        TRUE

    WHILE 

       DO-SOMETHING

    REPEAT

     

    -OR-

     

    BEGIN 

       DO-SOMETHING

       TRUE?

    WHILE REPEAT 

     

    -OR-

     

    BEGIN 

       DO-SOMETHING

       AND-SOMETHING-ELSE

       AND-ANOTHER?

    WHILE ( tos=true do) 

        LOTS-MORE STUFF

        ETC. ETC. ETC.

        THAT-THING-I-FORGOT

     REPEAT 

     

     

     

     

    • Like 2

  14. I revisited this 32 bit timer idea with fresh eyes and I think I have something that works well.

     

    The idea is the 9901 can only time 349 mS.

    The interrupt happens every 16mS but not reliably.

    So...

     

    Let's read the timer every interrupt and add it's value to a 32 bit accumulator variable.

    It's not likely the interrupts will be off for 300 milli-seconds at least in my system.

    That let's us keep a very big number as a timer. :) 

    It's spinning away here on the screen.

     

    NEEDS MOV,    FROM DSK1.ASM9900
    NEEDS ISR'    FROM DSK1.ISRSUPPORT
    
    CREATE T32  0  ,  0  ,  \ 32 bit timer accumulator
    
    CODE TIMERISR
    \ read the timer, which runs continuously in Camel99 Forth
             0 LIMI,
             R12 2 LI,
            -1 SBO,
             R0 14 STCR,
            -1 SBZ,
             2 LIMI,
    
    \ add timer value to 32 bit accumulator
             R0  T32 2+ @@ ADD,
             OC IF,
                T32 @@ INC,
             ENDIF,
             RT,
    ENDCODE
    
    ISR' TIMERISR INSTALL
    

     

    • Like 4

  15. 44 minutes ago, senior_falcon said:

    Does anyone has another font that works well in the 6x8 character size used by the text modes?

    I use this one which I found here in the GIF library.

    You probably already have it.  

    I think I tweaked the '0' and maybe a couple of letters and added a 40 column TI logo in the lower characters. (CHARS 11 to 20) 

    FONT0230

    • Like 2

  16. 6 hours ago, apersson850 said:

    OK. That means that your code does run with interrupt service enabled, or the timing wouldn't work. When my program is running interrupts are disabled, so it saves some fraction there. My super-duper 99 also includes a real time clock, based on a clock chip from National, with a resolution of one millisecond. I check the time before and after launching the sort procedure, and get the time from that.

     

    I've written a library unit called realtime, which allows access to my clock. The clock is my own design, so I had to make the drivers for it, of course. When the program includes uses realtime, a timer data type becomes available. The unit supports dynamic creation and disposal of timers. When a timer has been created, it can be reset, started and stopped. And read, of course.

     

    Here is a syntactically incorrect example, which just shows the principle of how I use them. I could stop the timer before reading it, but it's not mandatory. Any number of timers can be created and run simultaneously.

     

    uses realtime;
    
    var timer: tmrtype;
      elapsed: timetype;
    
    begin
      new(timer);
      tmrreset(timer);
      tmrstart(timer);
      quicksort(n,array);
      tmrread(timer,elapsed);
      with elapsed do
        write(hour); write(minute); write(second); writeln(fraction);
      end;
      dispose(timer);
    end.

     

    Very nice. That's the way to do it. Hardware!  :)

     

    I have the 9901 timer running continuously in Camel99 Forth and I use it primarily for timing the "MS" word in Forth which waits for milli-seconds.

    ( I limit resolution to 10mS because I poll it in Forth (slow) and it also yields to any other tasks while it's waiting)

     

    ( the code below is in my Cross-compiler Forth. That's the reason for the special incantations.

    [CC] "cross-compiling" lets me use the host Forth interpreter for immediate commands

    [TC]  "target-compiling" All definitions goe into the target image )

     

    \ TICKTOCK.HSF  TMS9901 hardware timer interface for Camel 99 Forth
    
    \ credit to: http://www.unige.ch/medecine/nouspikel/ti99/tms9901.htm#Timer
    \ impovements based on code from Tursi Atariage
    \ TMR! now loads from the Forth stack
    \ Jan 31, 2021 simplified JIFFS
    \ Dec 2021  removed JIFFS , replaced with TICKS which gives ~10mS resolution
    
    \ timer resolution:  64 clock periods, thus 64*333 = 21.3 microseconds
    \ Max Duration    :  ($3FFF) 16383 *64*333 ns = 349.2 milliseconds
    CROSS-ASSEMBLING
    [CC] DECIMAL [TC]
    CODE TMR!   ( n -- )         \ load TMS9901 timer from stack
                 0 LIMI,
                 R12 CLR,        \ CRU addr of TMS9901 = 0
                 0   SBO,        \ SET bit 0 to 1, Enter timer mode
                 R12 INCT,       \ CRU Address of bit 1 = 2 , I'm not kidding
                 TOS 14 LDCR,    \ Load 14 BITs from TOS into timer
                -1  SBZ,         \ reset bit 0, Exits clock mode, starts decrementer
                 2 LIMI,
                 TOS POP,
                 NEXT,
                 ENDCODE
    
    CODE [email protected]   ( -- n)         \ read the TMS9901 timer
                 0 LIMI,
                 TOS PUSH,
                 R12 2 LI,      \ cru = 1 (honest, 2=1)
                -1 SBO,         \ SET bit 0 TO 1, Enter timer mode
                 TOS 14 STCR,   \ READ TIMER (14 bits)
                -1 SBZ,         \ RESET bit 1, exit timer mode
                 2 LIMI,
                 NEXT,
                 ENDCODE
    
    
    [CC] DECIMAL [TC]
    : TICKS  ( n -- )   \ n must be less than 4000. 4000 TICKS ~= 100 mS
                [email protected] >R
                BEGIN
                  PAUSE
                  [email protected] [email protected] - ABS  OVER >
                UNTIL
                R> 2DROP
    ;
    
    : MS   ( n -- ) 10 /  0 ?DO  420 TICKS LOOP ;
    
    [CC] HEX [TC]

     

    I also use it for testing Assembly language code routines. Of it course it rolls over every 349 mS or so. :( 

    I explored some code last year that reads the 9901 on the VDP interrupt and adds the difference from last reading to a 32bit int. I didn't go back to it to really test it.

    I should probably look into making that work as it would be a "reasonably" accurate fined grained timer.

     

    For a time I had a an 8K RAM chip in my SuperCart that had battery backed-up clock built in.  The batteries were dead or dying and it was unreliable so I took it out.

    In Classic99 I could read the CLOCK file but that would be best for very long duration timing.

     

    Your solution is the best of the lot but iI suppose part of the "fun" is trying make it work with the 1978 hardware.  

    (sometimes its not fun) :) 

     

     

    • Like 3

  17. 51 minutes ago, apersson850 said:

    Nice to see you got it to work!

     

    Yes, the algorithm works better on random data. Data where almost all values are the same, except a few, or already sorted data, or reverse sorted - these take longer time to process. That has to do with how the sublists are created in these cases.

     

    At the same time it's interesting to see the time difference due to the memory. It's true that my machine has 64 K RAM 16-bit wide, with no wait state generation. My random list was sorted in about 0.25 seconds, so about half the time. That corresponds to my earlier exterments ,where I found the computer to be roughly twice as fast, when running assembly programs where both the workspace and the code is in expansion memory, which normally is 8-bit wide, with six cycles per memory word access, vs. two cycles for my computer. My reversed list was sorted in about 1.4 seconds or so.

     

    What do you use to do the timing of the procedure?

     

    And finally, when it comes to "the regular rates" - don't forget where this came from! 😁

    LOL.  Yes indeed. I put your handle in my version of the code for that very reason.  Willsy has to pay full freight!. :) 

     

    The timing is done using the screen timeout counter and scaling it. 

    It's not good if you are doing a lot of VDP actions because interrupts get stopped too frequently. But on code like this it real time tracks very well.

     

    So the timing on Classic99 is very close to real iron from what I can measure so your numbers with your super-duper TI-99 make sense versus these. 

    Thanks again for the code. It was great fun. The first Assembly attempt showed  99 errors.  How's that for a coincidence?

     

    Here is the ELAPSE timer code in Forth

     

    Spoiler
    \ ELAPSE.FTH  elapsed time measurment words
    \ Thanks to Tom Zimmer for the good ideas in FPC circa 1990
    \ Ported to HsForth 08MAR91  Brian Fox Canada
    
    \ Ported to CAMEL99 Nov 29 2017, 
    \ Simplified with SEXTAL Dec 6 2018
    \ Good for 9 minutes maximum duration
    
    \ *** YOU CANNOT CALL KSCAN WHILE TIMING ***
    
    HEX
    83D6 CONSTANT TICKER   \ screen timeout counter increments by 2 /16mS
    
    DECIMAL
    : SEXTAL   6 BASE ! ;
    : <:>     [CHAR] : HOLD ;
    : <.>     [CHAR] . HOLD ;
    : TIME$   ( n -- addr len) \ string output is more flexible
              BASE @ >R
              \         100ths        secs           minutes
              0 <#  DECIMAL # # <.> # SEXTAL # <:> DECIMAL #S  #>
              R> BASE ! ;
    
    : .ELAPSED ( -- ) 
               TICKER @ 5 6 */ TIME$
               CR ." Elapsed time ="  TYPE ;
    
    : ELAPSE   ( -- <text> ) 1 PARSE  TICKER OFF  EVALUATE .ELAPSED ;
    

     

     

    • Like 2

  18.  

    2 minutes ago, Willsy said:

    Wow! That's awesome - I might have to steal that! Great job :thumbsup: 

    Of course my regular rates apply. 🤣

     

    Have fun. I bet you have a ton of Assembler code you could test this way. The linker as it exists can only do 8K total of code but that's a shit-ton for testing purposes.

    And all the data could be in your Forth space anyway. 


  19. OK. Now how cool is this video.  :) 

     

    I did some editing on the Quicksort for Pascal so it would assemble under the TI assembler.

    I loaded my sorting test set.

    Link the object file and run and time it from the Forth command line.

    AND... I just had to change the PASCAL stack register to Camel99's stack register in the code, and the Forth stack pointer came across into the external program. 

     

    The time of 2.66 seconds slower than the original because @apersson850 's  machine has 16 bit memory I believe.

    This is running completely in 8 bit RAM, workspace and all. 

     

    Here is the slightly altered source file.

    Spoiler
    
    *        TITL "QUICKSORT FOR INTEGERS"
    
    * Procedure sorting integers in ascending order
    * Called from Pascal host by QSORT(A,N)*
    * Declared as PROCEDURE QSORT(VAR A:VECTOR* N:INTEGER)* EXTERNAL*
    * Vector should be an array [..] of integer*
    
    * Author: @APERSON850 on Atariage.com
    
    * Modified for TI Assembler, to be called from Camel99 Forth
    * 2022 Brian Fox
    
    *--------------
    * Workspace registers for subroutine at START
    *
    * R0  Array base pointer
    * R1  Array end pointer
    * R2  L
    * R3  R
    * R4  I
    * R5  J
    * R6  KEY
    * R7  Temporary
    * R8  Temporary
    * R9  Subfile stack pointer
    * R10 Main stackpointer
    * R11 Pascal return address (P-system WS)
    * R12 Not used
    * R13 Calling program's Workspace Pointer
    * R14 Calling program's Program Counter
    * R15 Calling program's Status Register
    *-----------------
    
    * The actual vector in the Pascal-program could be indexed [1..n]
    * This routine assumes only that n indicates the number of elements, not the
    * last array index.
    
    
             DEF  QSORT
    
    LIMIT    EQU    16               *Quick- or Insertionsort limit
    
    *  * Don't need this for Forth **
    *        BLWP @QSORT
    *        AI   R10,4              *Simulate pop of two words
    *         B    *R11              *Back to Pascal host
    
    QSORT    DATA SORTWS,START       *Transfer vector for Quicksort
    
    START    MOV  @12(R13),R10       *Get stackpointer from Forth's WP
             LI   R9,ENDSTK          *SUBFILE STACKPOINTER
             MOV  *R10+,R1           * POP PARAMETER N
             MOV  *R10+,R0           * POP ARRAY POINTER
             DEC  R1
             SLA  R1,1
             A    R0,R1              *CALCULATE ARRAY ENDPOINT
    
             MOV  R0,R2              *L:=1
             MOV  R1,R3              *R:=N
             MOV  R1,R7
             S    R0,R7
             CI   R7,LIMIT
             JLE  INSERT             *FIGURE OUT IF QUICKSORT IS NEEDED
    
    MNLOOP   MOV  R2,R7
             SRL  R7,1
             MOV  R3,R8
             SRL  R8,1
             A    R8,R7
             ANDI R7,>FFFE           *R7:=INT((L+R)/2)
             MOV  *R7,R8
             MOV  @2(R2),*R7
             MOV  R8,@2(R2)          *A[(L+R)/2]:=:A[L+1]
    
             C    @2(R2),*R3
             JLT  NOSWP1
             MOV  @2(R2),R8
             MOV  *R3,@2(R2)
             MOV  R8,*R3             *A[L+1]:=:A[R]
    
    NOSWP1  C    *R2,*R3
             JLT  NOSWP2
             MOV  *R2,R8
             MOV  *R3,*R2
             MOV  R8,*R3             *A[L]:=:A[R]
    
    NOSWP2   C    @2(R2),*R2
             JLT  NOSWP3
             MOV  @2(R2),R8
             MOV  *R2,@2(R2)
             MOV  R8,*R2             *A[L+1]:=:A[L]
    
    NOSWP3   MOV  R2,R4
             INCT R4                 *I:=L+1
             MOV  R3,R5              *J:=R
             MOV  *R2,R6             *KEY:=A[L]
             JMP  INCLOP
    
    INRLOP   MOV  *R4,R8             *LOOP UNWRAPPING
             MOV  *R5,*R4
             MOV  R8,*R5             *A[I]:=:A[J]
    
    INCLOP  INCT R4                 *I:=I+1
             C    *R4,R6
             JLT  INCLOP            *A[I]<KEY
    
    DECLOP   DECT R5                 *J:=J-1
             C    *R5,R6
             JGT  DECLOP            *A[J]>KEY
    
             C    R4,R5
             JLE  INRLOP           *IF I<=J THEN CONTINUE
    
    OUT      MOV  *R2,R8
             MOV  *R5,*R2
             MOV  R8,*R5             *A[L]:=:A[J]
    
    DEL1     MOV  R5,R7              *Quicksort subfiles?
             S    R2,R7              *R7:=J-L
             MOV  R3,R8
             S    R4,R8
             INCT R8                 *R8:=R-I+1
             CI   R7,LIMIT
             JH   DEL2
             CI   R8,LIMIT
             JH   DEL2
    
             CI   R9,ENDSTK          *LVL=0?
             JEQ  INSERT             *No more Quicksorting at all?
    
             MOV  *R9+,R2            *POP L
             MOV  *R9+,R3            *POP R
             JMP  MNLOOP
    
    DEL2     C    R7,R8              *Determine what is small and large subfile
             JL   ELSE2
    
             MOV  R2,@LSFL
             MOV  R5,@LSFR
             DECT @LSFR
             MOV  R4,@SSFL
             MOV  R3,@SSFR
             JMP  DEL3
    
    ELSE2    MOV  R4,@LSFL
             MOV  R3,@LSFR
             MOV  R2,@SSFL
             MOV  R5,@SSFR
             DECT @SSFR
    
    DEL3     CI   R7,LIMIT           *Is small subfile big enough to be sorted by
             JLE  THEN3              *Quicksort?
             CI   R8,LIMIT
             JH   ELSE3
    
    THEN3    MOV  @LSFL,R2           *Don't Quicksort small subfile, only large
             MOV  @LSFR,R3
             JMP  MNLOOP
    
    ELSE3    DECT R9                 *Stack large subfile
             MOV  @LSFR,*R9          *PUSH R
             DECT R9
             MOV  @LSFL,*R9          *PUSH L
             MOV  @SSFL,R2           *Sort small subfile
             MOV  @SSFR,R3
             JMP  MNLOOP
    
    *
    * Insertion Sort finishing up
    *
    INSERT   MOV  R1,R4
             DECT R4                 *I:=N-1
             C    R4,R0
             JL   LEAVE              *Check if any looping at al
    
    FORI     C    @2(R4),*R4
             JGT  NEXTI              *If next is greater than this, it's OK
    
             MOV  *R4,R6             *KEY:=A[I]
             MOV  R4,R5
             INCT R5                 *J:=I+1
    
    WHILE    MOV  *R5,@-2(R5)        *A[J-1]:=A[J]
             INCT R5                 *J:=J+1
             C    R5,R1
             JH   ENDWHL             *J>N?
             C    *R5,R6             *A[J]<KEY?
             JLT  WHILE
    ENDWHL MOV  R6,@-2(R5)           *A[J-1]:=KEY
    NEXTI    DECT R4
             C    R4,R0              *Check if passed array base point
             JHE  FORI
    
    LEAVE    RTWP                    * Return Forth workspace
    *        AI   R6,4               * remove parameters from Forth stack
             B    @R10               * Branch to Forth interpreter
    
    *--------------
    * DATA AREA
    
    SORTWS   BSS  >20             *Workspace for sorting routine
    SUBSTK   BSS  >40             *Internal subfile stack
    ENDSTK   EQU  SUBSTK+>40      *End of that stack
    
    * variable used by quicksort
    LSFL     DATA   0             *Large SubFile Left pointer
    LSFR     DATA   0             *Large SubFile Right pointer
    SSFL     DATA   0             *Small SubFile Left pointer
    SSFR     DATA   0             *Small SubFile Right pointer
    
             END
    

     

     

    • Like 3
×
×
  • Create New...