Jump to content

TheBF

+AtariAge Subscriber
  • Posts

    4,469
  • Joined

  • Last visited

Posts posted by TheBF

  1. Oh I see now that I actually read the rest of your code. You use the quotient as the 1st digit. 

    OK. Very specific number conversion.

    I will leave it with you as an exercise to to make an integer version. :) 

     

    I am happy that I am able to help in some small way. 

     

  2. And then remember that an HEX  integer can hold up to 4 digits, a HEX byte can be up to 2 digits.

    So if you read the file as integers you need a 4 digit buffer, if as bytes a 2 byte buffer

    You put the results of the characters into the buffer backwards. ie: right to left

     

    So you need to read a byte, process 2 chars into the buffer. The leftmost chars could be a zero. 

    That is why I recommended the other procedure to "make a number" which will be a little buffer of characters that you can print to the screen. 

     

  3. 18 minutes ago, Vorticon said:

    Oops... I'm dividing by 256 instead of 16.

    write(dec2hex(buffer[i] div 16));
    write(dec2hex(buffer[i] mod 16));

     

    Also I forgot to subtract 10 from the A-F conversion

    if n >= 10 then
       dec2hex := chr(n + 65 - 10);

     

    Still not working though... What else am I missing???

    Ok one bug down. :)

    Only the results of the mod operation , passed through the digit convertor goes into the buffer. 

     

    This name dec2hex is a bit misleading since a binary number you are processing is neither BASE 10 nor  BASE 16. 

    This process of converting it to text, is what will determine if it decimal or hexadecimal or binary or whatever. 

     

    This is should work and it's smaller!  

    function todigit(n : integer) : char;
    begin
     if n > 9 then
        n := n + 7
       todigit := chr(n + 48);
    end; (*todigit *)
  4. 52 minutes ago, jrhodes said:

    Well this is just lovely... My windows 10 pc installed a update, and now I get a black screen and a mouse pointer upon logging in. I can Ctrl alt del and get a task manager.

    A preliminary search online seems to suggest that I run" SFC /SCANNOW " from administrative powershell.

    <sigh> 😔

    My update wasn't that bad but there seems to be a bug in the file windows where they "stop responding".  Not like the file system is important or anything...

    • Confused 1
  5.  

    I'm biased by you know what, but you might try factoring this into some smaller pieces.

    Here are the pieces used in a Forth system to chop an integer into a set of ascii digits. 

     

     TODIGIT converts a binary digit to ascii but handles the gap between 0 .. 9 and A .. F. 

     

    Then you need a way to divide and modulo-divide an integer by the radix, in this case 16.

    The quotient is retained,  the modulus is passed to TODIGIT and the ascii character is placed in a buffer starting from right to left. 

    Lather rinse and repeat until there is nothing left in the quotient. 

     

    In very bad pseudo  code: (I don't think like this any more) :) 

     

    function TODIGIT  ( X : integer) 

               var out : char

               IF   X > 9 THEN  X=X+7  { handles ABCDEF } 

               out := x + 48  { ascii 0 } 

              return out

    end;    

     

    procedure MAKE_NUMBER ( inputinteger ) 

               var hp, quotient , modulus , length : integer 

               var hbuff()   6 bytes                 { should be global and big enough to hold a 16 bit number } 

               hp := 6                                   { called a hold pointer in Forth } 

               length:=0 

              repeat 

                 quotient := input / 16            { if you made 16 a global variable you code convert to different bases } 

                 modulus := input mod 16 

                 hbuff(hp):=todigit(modulus)  { put the ascii digit in the buffer } 

                 hp:=hp-1                             { move the pointer to the next digit location

                 inc(length) 

              until quotient=0 

              return hbuff() , length    { length needs to global to make this easy to do in Pascal ? )

    end;

               

    Maybe that will give some helpful hints. ???   

          

  6. When I reviewed the thread on this program I found the results of this program compiled with the BASIC compiler. 

      On 3/24/2011 at 8:59 PM, unhuman said:

    Compiled it kicks Forth's butt!

           "Maybe versus Weiand's Forth:

            Compiled-Basic: 1m40s

            Weiand-Forth: 3m"

     

    But the Weiand Forth version was written by Lucien who admitted that he was new to Forth and the algorithm is completely different. 

     

    Now, with a line by line translation, we can see that indirect-threaded Forth and compiled BASIC are running about the same speed.

    If we compiled my BASIC version with simpler printing it would probably take the extra 20 seconds off or maybe more.

     

    @Lee Stewart wrote a version using Forth idiomatically at it runs on my system in 1:02 with the default scroll code.

    If increase the scroll buffer to the entire screen it runs in 55 seconds, but that's wasteful.

     

    LOL. I am moving over to the old Seven's problem post. I wonder if my JIT can swallow the new version... ? ;) 

     

     

     

     

     

     

  7. Outsider curiosity question. 

     

    Do you need any special incantations to declare a leaf sub-routine? 

    Perhaps the compiler simply looks for any calls within the sub-routine and modifies the entry and exit code?

  8. 2 minutes ago, Lee Stewart said:

     

    In getbyte, if the MSB of R6 is ignored, this is not a problem, but, as far as I can tell, the MSB of R6 is indeterminate unless it has been cleared before invoking the procedure.

     

    ...lee

    You got those eagle  eyes.

     

    So this is similar to the case of C@ in our Forth systems where we would use:

     

    SRA R6,8   rather than  SWPB R6 

     

    The GCC team was fighting with this for a while too. 

    • Like 2
  9. That's a strange bug for sure. 

     

    Something I notice is that you are shuffling the data across a few registers.

    You could be reading more directly to the final destination.  Not sure it fix the bug but it will reduce the time spent in getbyte.

    STCR *Rx,8  
    INC   Rx

     

    or maybe (have not tried this but I think it's valid)

    STCR *R10+,8

    That way the data goes into the buffer directly with auto incrementing.

     

  10. Still working on cleaning up my repository and I came across the old sevens problem. 

     

    I always wanted a version that was a literal translation of the BASIC so I did one. 

    It's fun to look at.  I did not use any tricks unless you call one DUP a trick. :) 

     

    Here is the TI BASIC version that runs in 27 minutes.  I am afraid I have lost the accreditation for it so speak up if it's yours. (Lucien?)

    Edit: Original program was written by @unhuman.

    I modified lines 340 to 360 to speed up printing. The original used HCHAR. 

     

    Spoiler
    1 rem for comparison to Forth
    2 rem TI BASIC version runs in 27mins and 20 seconds 
    10 OPEN #1:"CLOCK"
    20 INPUT #1:A$,DATE$,START$
    100 DIM A(256)
    110 PRINT "7's Problem"
    120 A(1)=7
    130 WIN=0
    140 POWER=1
    150 NUMLEN=1
    160 POWER=POWER+1
    170 PRINT "7 ^";POWER;"IS:"
    180 CARRY=0
    190 INAROW=0
    200 FOR I=1 TO NUMLEN
    210    A(I)=A(I)*7+CARRY
    220    CARRY=INT(A(I)/10)
    230    A(I)=A(I)-CARRY*10
    240    IF A(I)<>7 THEN 290
    250    INAROW=INAROW+1
    260    IF INAROW<>6 THEN 300
    270    WIN=1
    280    GOTO 300
    290    INAROW=0
    300 NEXT I
    310 A(I)=CARRY
    320 IF CARRY=0 THEN 340
    330 NUMLEN=NUMLEN+1
    340 FOR I=NUMLEN TO 1 STEP -1
    350     PRINT CHR$(A(I)+48);
    360 NEXT I
    363 REM we're done get the time
    365 INPUT #1:A$,DATE$,END$
    370 PRINT ::
    380 IF WIN<>1 THEN 160
    390 PRINT "WINNER IS 7 ^";POWER
    394 Print "Sevens started:";START$
    400 PRINT "Ended at ";END$
    410 CLOSE #1
    420 END

     

     

    And here is a Forth literal translation that runs in edit:  1 minute 10 seconds on my indirect threaded system. 

    ( Re-timed using a stop-watch because the there was too much VDP I/O playing with the ISR timer)

    I have included the BASIC code as comments. 

    It would be faster if I replace the VARIABLEs with VALUEs  but this is vanilla Forth. 

    Most systems have character array creator CARRAY or it can be made easily enough. 

     

    Spoiler
    \ literal translation of BASIC program to Forth 
    
    INCLUDE DSK1.TOOLS 
    INCLUDE DSK1.ELAPSE 
    INCLUDE DSK1.ARRAYS 
    
    DECIMAL 
    : ?BREAK   ?TERMINAL ABORT" *BREAK*" ; 
    
    \ must define all data before use
    VARIABLE WIN 
    VARIABLE POWER 
    VARIABLE NUMLEN 
    VARIABLE CARRY 
    VARIABLE INAROW 
    VARIABLE NDX   ( transfers loop index out of DO LOOP )
    
     256 CARRAY ]A                  \  100 DIM A(256)
    : RUN 
      CR ." 7's Problem "           \ 110 PRINT "7's Problem"
      0 ]A 256 0 FILL               \ init ]A to zero
      7 0 ]A C!                     \ 120 A(1)=7
       WIN OFF                      \ 130 WIN=0
      1 POWER !                     \ 140 POWER=1
      0 NUMLEN !                    \ 150 NUMLEN=1
      BEGIN POWER 1+!               \ 160 POWER=POWER+1
        ." 7 ^" POWER @ . ." IS:"   \ 170 PRINT "7 ^";POWER;"IS:"
        ?BREAK 
        CARRY OFF                   \ 180 CARRY=0
        INAROW OFF                  \ 190 INAROW=0
        NUMLEN @ 1+ 0               \ 200 FOR I=1 TO NUMLEN
        DO    
            I NDX !                 \ copy I for later 
            I ]A C@ 7 *  CARRY @ +  \ 210 A(I)=A(I)*7+CARRY
    \ We avoid some math with divide & mod function 
            0 10 UM/MOD  CARRY !    \ 220 CARRY=INT(A(I)/10)
            I ]A C!                 \ 230 A(I)=A(I)-CARRY*10
            I ]A C@ 7 =             \ 240 IF A(I)<>7 THEN 290
            IF     
                INAROW 1+!          \ 250 INAROW=INAROW+1
                INAROW @ 6 =        \ 260 IF INAROW<>6 THEN 300
                IF                
                  WIN ON            \ 270 WIN=1
                  LEAVE 
                THEN                
            ELSE                    \ 280 GOTO 300 
                INAROW OFF          \ 290 INAROW=0
            THEN 
            ?BREAK 
        LOOP                        \ 300 NEXT I
    
        CARRY @ 
        DUP NDX @ 1+ ]A C!          \ 310 A(I)=CARRY
        IF                          \ 320 IF CARRY=0 THEN 340
            NUMLEN 1+!              \ 330 NUMLEN=NUMLEN+1
        THEN 
        CR                          \ replaces PRINT 
        0 NUMLEN @                  \ 340 FOR I=NUMLEN TO 1 
        DO   
          I ]A C@ >DIGIT (EMIT)     \ 350 PRINT CHR$(A(I)+48);
        -1 +LOOP                    \ 360 NEXT I ( STEP -1)
        CR CR                       \ 370 PRINT ::
        WIN @                       \ 380 IF WIN<>1
      UNTIL                         \     THEN 160
      ." Winner is 7 ^" POWER @ .   \ 390 PRINT "WINNER IS 7 ^";POWER
    ;                               \ 420 END
    

     

     

     

    • Like 3
    • Thanks 1
  11. When I first got the system running it compiled very slowly.  I did a little test typing 1 to 9 with a space between each digit and hitting enter.

    This was a worst case search and it was something like 3 or 4 seconds. 

    I wrote (FIND) in Assembler and the same test takes just under 1 second.  Brad made Camel Forth for small size so somethings suffer. 

     

    Interesting stuff on the hashing. PolyForth used to limit the dictionary to the length, three characters and a hash value for the rest.

    I can't remember the details but that was for compact size. 

    F83 created 4 search threads and put each word in a different thread by hashing the 1st character. This speeds up the average search time by 4.

    Forth Inc. uses 8 search threads.   I have considered doing a 4 way hashed dictionary. 

     

    I did make a hash table of the entire dictionary to compare search times using only Forth code and it was 3 times faster than what I have now.

    Hashing is amazing when you find the right one.

     

    • Like 3
  12. Part of the innards of a Forth system is some way to "parse" through source, extracting one space delimited word at a time. 

    Traditionally this was done with BL WORD.  Looks simple but if you peek behind on the curtain on any Forth system there is fair bit of code behind WORD. 

     

    Here is a modern way to code PARSE-NAME that I found on the Forth Standard site. 

    PARSE-NAME is what they use these days instead of BL WORD.   

    This code is note-worthy to me because of the word XT-SKIP which is like SKIP but instead of testing for a character match, XT-SKIP runs a piece of code so it can compare any range of characters.

    In this case any character less than a space is white space and anything not white space is a valid character but we could put any code in there. 

     

    This appears to be a lot less code than I used in CAMEL99 Forth but on the other hand I have WORD  PARSE and PARSE-WORD and I make PARSE-NAME

    with:

    : PARSE-NAME  BL PARSE-WORD ;

     

    Anyway I thought some folks might want to see how modern languages are influencing Forth thought leaders

    and how Forth can replicate those "mapping" features without much trouble.  (this code compiles on Camel99 FORTH)

     

    \ PARSE-NAME  from https://forth-standard.org/standard/core/PARSE-NAME
    
    : white?  ( c -- f )  BL 1+ U< ; \ space and below are white chars
    : -white? ( c -- f ) white? 0= ; \ everything above are not
    
    : xt-skip ( addr1 n1 xt -- addr2 n2 )
     \ skip all characters satisfying xt ( c -- f )
       >R
       BEGIN
         DUP
       WHILE
         OVER C@ R@ EXECUTE
       WHILE
         1 /STRING
       REPEAT THEN
       R> DROP ;
    
    : PARSE-NAME ( "name" -- c-addr u )
       SOURCE >IN @ /STRING
       [']  white? xt-skip OVER >R
       ['] -white? xt-skip ( -- end-word restlen) ( r: start-word )
       2DUP 1 MIN + SOURCE DROP - >IN !
       DROP R> TUCK - ;

     

    • Like 1
  13. 2 hours ago, apersson850 said:

    In the TMS 9900 documentation you have the basic number of clock cycles given. Reading the instruction itself is included, but on the TI 99/4A, reading the instruction has four wait states unless the instruction is in the console's ROM or in the RAM PAD (>8300). You have to add that. Normally you have the registers in RAM PAD so they don't add anything.

    In that same table, there are address modifications of type A and B. If any of those applies, then you add the clock cycles from A and/or B to the basic number of cycles.

    Next you look at how many memory accesses there are associated with A and B. If these acceses go elsewhere than to ROM or RAM in the console, you have to add four wait states for each cycle there too.

     

    This is why an instruction using more advanced addressing easily can grow from 14 to 30 cycles. It's also the reason for why internal memory expansion on 16-bit wide bus can increase speed with up to 110%, if you have both code and registers in expansion RAM.

    Yes, I knew about the A and B address modifications so my numbers in the code reflect that. The dirty details of how bad it gets with TI-99 expansion RAM is where I stopped. 

    However if the speedup can be 110% then using a 2X more cycles as a "rule of thumb" is reasonable I guess. 

  14. You know it's a wonder I don't have a much flatter forehead. :)

     

    I was thinking about how to transfer binary data over RS232 now that I have this reliable com port receiver.

    I realized that the way I implement KEY? would be a problem because my version tests for a key and returns the ASCII value pressed OR returns a zero if no key was pressed. 

    But binary data can contain zeros.  So I started wondering how does Forth handle that?

     

    I went to the Standard and see this:

     

    10.6.1.1755

    KEY?  key-question
    ( -- flag )
    If a character is available, return true. Otherwise, return false.

     

    Oops!  I implemented this incorrectly.  KEY? is just the test.  KEY is what reads the character.

    I thought I would be "efficient" and while I was checking, just read the character as well. 

     

    By using KEY?  and KEY together you can collect binary data no problem.

    If you do it my way... not so much.

     

    Time for some kernel fixing.

     

     
    • Like 2
  15. 2 hours ago, apersson850 said:

    Did you do account for the wait-state generation in the TI 99/4A when you did your cycle math?

     

    Anyway, for top speed nothing but assembly will work. Even that's tight, as your estimates show.

    I was more thinking along the line of implementing an ISR that would read incoming characters and stuff them in the reception buffer. Do that, increment the buffer pointer and control the CTS pin accordingly and that's about it. Checking if there are characters available and then signal the semaphore could be done on the VDP interrupt, if one would prefer. Then I'm not sure at what rate characters could be read out from the buffer. Some experimentation would probably be required.

    No I counted from the book so it's the very best case which is why I doubled the number.  I don't actually know how to calculate the slow down from the the TI-99 wait state generator.

    I took a look at the Classic99 dis-assembler and see between 0 difference from the text book for a register in the scratch-pad to what seems like double the cycles in some cases.

     

    Yes indeed my ISR does those things and hits the CTS pin when the buffer is 1/2 full. 

    Again the code below  is in RPN assembler and Forth but I think it could be worked into the Pascal environment from what you have said about working with interrupts. 

    There are two big pieces.  The "installer" that sets up the magic to trick the console ISR and the actual ISR to collect data into the queue. 

     

    It's all derived work from other people but it's working well now so when the time comes it might be useful to @Vorticon to translate it. 

     

    Spoiler
    \ RS232/1 Interrupt Handler for CAMEL99 Forth   B Fox Feb 14 2019
    
    \ Feb 2024- make this run on CAMELTTY Forth 
    
    NEEDS DUMP FROM DSK1.TOOLS    \ DEBUG ONLY
    NEEDS MOV, FROM DSK1.ASM9900
    
    \ *************************************************************************
    \ *   Adaptation of Jeff Brown / Thierry Nouspikel (sp) idea to leverage
    \ *   the ROM-based ISR to service external interrupts (RS232 in our case)
    \ *   within the VDP interrupt framework.
    \ *   Based on code by Insanemultitasker ATARIAGE
    
    \ Changes:
    HEX
                 83C0 CONSTANT ISRWKSP
     CARD @ UART @ +  CONSTANT COM1
    
    : (R4)    R4 () ;  \ syntax sugar for Forth Assembler 
    
    \ ************************************************************
    \ simple circular Q management
      40       CONSTANT QSIZE
      QSIZE 1- CONSTANT QMASK    \ circular mask value
     
     CREATE Q  ( -- addr)   QSIZE CELL+ ALLOT 
    
    \ Queue pointers
      VARIABLE QHEAD
      VARIABLE QTAIL
    
    : QCLEAR     Q QSIZE 0 FILL   QHEAD OFF   QTAIL OFF ;
    
    \ ************************************************************
    \ * QKEY? - Read character from 'Q' at index 'QHEAD'
     HEX
     CODE QKEY? ( -- c | 0 )         \ 0 means queue empty
           TOS PUSH,                 \ make space in the TOS cache (R4)
           TOS CLR,                  \ FLAG to say no char ready
           QHEAD @@ QTAIL @@ CMP,
           NE IF,                    \ head<>tail means char waiting
               QHEAD @@ W MOV,       \ get queue head index to W
               Q (W) TOS MOVB,       \ get char from Q -> TOS
               TOS SWPB,             \ move to other side of register
               W INC,                \ inc the index
               W QMASK ANDI,         \ wrap the index
               W QHEAD @@ MOV,       \ save the new index
           ELSE,
           \ queue is empty...
               CARD @@ R12 MOV,      \ can't assume R12 is correct
               5 SBZ,                \ set -CTS line LOW to get more data
           ENDIF, 
           NEXT,
    ENDCODE
    
    \ **************************************************************
    \ * ISR is in workspace 83C0. ONLY R3 & R4 are free to use!!!
    DECIMAL
    CREATE TTY1-ISR ( *isr with hardware handshake * )
           ISRWKSP LWPI,                                           \   10
           R12 CLR,          \ select 9901 chip CRU address        \   10
           2 SBZ,            \ Disable VDP int prioritization      \   12
           R11 SETO,         \ 3.5.16 hinder screen timeout        \   10 
           R12 COM1 LI,      \ select card1+uart1                  \   12 
           QTAIL @@ R4 MOV,  \ Queue tail pointer ->R4             \   22
           16 TB,            \ interrupt received?                 \   12
           EQ IF,            \ Yes; enqueue char                   \   10
                Q (R4) 8 STCR,  \ read byte into Q                 \   52
            \  *** manage Queue pointer ***
                R4 INC,            \ bump the index                    10
                R4 QMASK ANDI,     \ wrap the index                    14
                R4 QTAIL @@ MOV,   \ save index in QTAIL               22
    
            \  *** test buffer status ***
                QHEAD @@ R4 SUB,   \ R4 has Qtail                      22
                R4 ABS,            \ R4 has byte count in Q            12
                R4 QSIZE 2/ CI,    \ 1/2 full?                         14 
                GTE IF,                                             \  10
                \ we can change CTS line by using a negative bit value
                   -27 SBO,          \ CTS line HIGH. I am busy!       12
                ENDIF,
           ENDIF,
           18 SBO,         \ clr rcv buffer, enable interrupts         12
           R12 CLR,        \ select 9901 chip CRU address              10
           3 SBO,          \ reset timer int                           12
           RTWP,           \ Return                                    14
                           \          104.6 uS                        314 
    
    \ *******************************************************************
    \ * Configure ROM ISR to pass through external interrupts as VDP interrupts
    \ *   (Jeff Brown/Thierry)
    
    HEX
    \ get address Forth's tos register (R4) so we can transfer ISR handler 
    \ to the ISR workspace 
    8300 4 CELLS + CONSTANT 'TOS
    
    CODE INSTALL ( ISR_address -- )
           0 LIMI,
           83E0 LWPI,       \ select GPL workspace 
           R14 CLR,         \ Disable cassette interrupt; protect 8379
           R15 877B LI,     \ disable VDPST reading; protect 837B
    
           ISRWKSP LWPI,    \ switch to ISR workspace
           R1 SETO,         \ [83C2] Disable all VDP interrupt processing
           'TOS @@ R2 MOV,   \ [83C4] set our interrupt vector from Forth R4
           R11 SETO,        \ Disable screen timeouts
    
           R12 CLR,         \ Set to 9901 CRU base
           BEGIN,
              2 TB,         \ check for VDP interrupt
           NE UNTIL,       
    
           1  SBO,          \ Enable external interrupt prioritization
           2  SBZ,          \ Disable VDP interrupt prioritization
           3  SBZ,          \ Disable Timer interrupt prioritization
    
           8300 LWPI,       \ return to the FORTH WS
           TOS POP,         \ refill stack cache register
           2 LIMI,          \ 3.2  [rs232 ints now serviced!]
           NEXT,            \ and return to Forth
    ENDCODE
    
    DECIMAL
    CODE ISRON ( uart -- )  \ * Turn on the 9902 interrupts
           0 LIMI,
           TOS R12 MOV,
           18 SBO,          \  Enable rs232 RCV int
           TOS POP,
           2 LIMI,
           NEXT,
    ENDCODE
    
    CODE ISROFF ( uart -- )  \ * Turn off the 9902 interrupts
           0 LIMI,
           TOS R12 MOV,      \ i.e., >1340
           18 SBZ,           \ Disable rs232 rcv int
           TOS POP,
           2 LIMI,
           NEXT,
    ENDCODE
    
    : ISR-I/O
          QCLEAR            \ reset Queue pointers, erase data
          KEY? DROP         \ clear any char from 9902
          COM1 ISROFF       \ just to be safe
          TTY1-ISR INSTALL
          ['] QKEY? >BODY  ['] KEY? !  \ patch KEY?' to read the queue
          COM1 ISRON ;
    
    CR .( Intalling ISR on port TTY1 ...)
    ISR-I/O 
    CR .( ISR recieve enabled)
    CR 

     

     

     

    • Like 2
  16. 7 minutes ago, apersson850 said:

    The interrupt signal has to come from the card, of course. Not the VDP.

    But I've not done any math about how frequently that system can respond to interrupts. The TMS 9900 doesn't execute too many instructions in a millisecond...

    Ok. That make sense. 

    My RS232 ISR executes in 314 cycles on a zero wait-state machine (105uS) from my counts from the 9900 manual.

    Looking at Classic99 I see about 50% overhead compared to the textbook cycles. ??

    But let's say it's 2X slower. 

    If so that would ~210uS.  If we are sending at 19,200bps full speed we have characters hitting about every 500uS, so that could consume over 40% of the CPU. 

    • Like 2
  17. Could this work with fast serial communication? At 9600 bps you have a byte coming in every millisecond or so.

    16mS interrupt time would not allow very fast communication or am I missing the important part somewhere?

  18. You are safe with that decision. I don't test RTS on receive side, I simply pull on the CTS line to keep Teraterm from overrunning TI-99. 

     

    It's RPN but you can see what I use that works in non-interrupt driven receive.

    Not sure how much difference it makes but I also disabled interrupts on the chance that a character would be missed because an interrupt stole the CPU's attention away from my more important stuff. 

    [CC] DECIMAL [TC]
    CODE KEY? ( -- n )          \  "com-key"
            0 LIMI,
            R12 RPUSH,           \ save R12 on return stack  *Needed?*
            CARD @@ R12 MOV,     \ set base address of CARD
            UART @@ R12 ADD,     \ add UART, >1300+40 = CRU address
            TOS PUSH,            \ give us a new TOS register (R4)
            TOS CLR,             \ erase it
    
    \ use negative CRU address to reach back to CARD base address >1300
            -27 SBZ,             \ CARD CTS line LOW. You are clear to send
            21 TB,               \ test if char ready
            EQ IF,
                TOS 8 STCR,      \ read the char
                18 SBZ,          \ reset 9902 rcv buffer
                TOS SWPB,        \ shift char to other byte
            ENDIF,
            -27 SBO,             \ CTS line HIGH. I am busy!
            R12 RPOP,            \ restore old R12  *Needed?*
            2 LIMI,
            NEXT,
            ENDCODE

     

     

     

    • Like 2
  19. 1 hour ago, FarmerPotato said:

    @TheBF

    In any 9902 code, it helps me to have the symbol names for CRU bits like XBRE, RTSON etc. 

    Me too but in Forth they take up space so I used a couple of the important ones and put a list of them in comments in my file. 

    1 hour ago, FarmerPotato said:

     

    Ive been very confused about RTS/CTS.  A lot of 4A software leaves RTS asserted during a session. But I saw that    T I source code only asserts RTSON before sending a character, then turns it back off. (The 9902 waits for CTS before actually transmitting, so I guess you don't have to.) 

    I have seen a lot of variations in interpretation of how all those signals are used in real world equipment. So you confusion is well founded.  It's an ancient spec from a time when data transmission was having to deal with lots of new kinds of gear. With the carrier detect signal it appears that RS232 was designed for modems. ?  

    Some equipment I have worked with ignores all of the handshakes and relies on really fast ISRs to catch every character in a healthy buffer and process the data faster than it can be delivered. Others handshake every character.

     

    1 hour ago, FarmerPotato said:

     

    Another puzzle is: why does the 4A's RS232 card implement a CTS output?

    I suspect the answer has to do with the 4A being wired like DCE instead of DTE. (Which is weird.) If it were acting as a DCE to another DTE, then it needs to acknowledge a RTS.  That's not symmetric. 

    The document that clarified what I should do was on T I's guide to interfacing UART chips and the "modern" PC DB9S connector. 

    Hit send too soon. 

    From what I see the 9902 doesn't have a CTS line, so it looks like TI built one on the card. ??

    Edit: Nope. That's not true. I just looked at the chip pinout. 

    So I have no idea why they did that.

     

    That is a very nice document. Thanks for sharing. 

    • Like 3
  20. I did some experiments to see how small I could make the queue buffer.  I reduced in steps down to 64 bytes,

    which means it fills up to 32 bytes with 32 more for over runs in case the sender doesn't recognize the handshake fast enough. 

     

    The TI did not drop one character. The video shows it compiling a pretty large file over the terminal with the small buffer.

     

     

    Here is what I will call a final version.  It has greatly simplified Q creation and a few instructions less. 

    BTW I tried writing QKEY? in Forth and it is a few bytes bigger. 

    I have to make a good machine Forth using the power of the 9900 inline code. 

    Spoiler
    \ RS232/1 Interrupt Handler for CAMEL99 Forth   B Fox Feb 14 2019
    
    \ Feb 2024- make this run on CAMELTTY Forth 
    
    NEEDS DUMP FROM DSK1.TOOLS    \ DEBUG ONLY
    NEEDS MOV, FROM DSK1.ASM9900
    
    \ *************************************************************************
    \ *   Adaptation of Jeff Brown / Thierry Nouspikel (sp) idea to leverage
    \ *   the ROM-based ISR to service external interrupts (RS232 in our case)
    \ *   within the VDP interrupt framework.
    \ *   Based on code by Insanemultitasker ATARIAGE
    
    \ Changes:
    HEX
                 83C0 CONSTANT ISRWKSP
     CARD @ UART @ +  CONSTANT COM1
    
    : (R4)    R4 () ;  \ syntax sugar for Forth Assembler 
    
    \ ************************************************************
    \ simple circular Q management
      40       CONSTANT QSIZE
      QSIZE 1- CONSTANT QMASK    \ circular mask value
     
     CREATE Q  ( -- addr)   QSIZE CELL+ ALLOT 
    
    \ Queue pointers
      VARIABLE QHEAD
      VARIABLE QTAIL
    
    : QCLEAR     Q QSIZE 0 FILL   QHEAD OFF   QTAIL OFF ;
    
    
    \ ************************************************************
    \ * QKEY? - Read character from 'Q' at index 'QHEAD'
     HEX
     CODE QKEY? ( -- c | 0 )         \ 0 means queue empty
           TOS PUSH,                 \ make space in the TOS cache register
           TOS CLR,                  \ FLAG to say no char ready
           QHEAD @@ QTAIL @@ CMP,
           NE IF,                    \ head<>tail means char waiting
               QHEAD @@ W MOV,       \ get queue head index to W
               Q (W) TOS MOVB,       \ get char from Q -> TOS
               TOS SWPB,             \ move to other side of register
               W INC,                \ inc the index
               W QMASK ANDI,         \ wrap the index
               W QHEAD @@ MOV,       \ save the new index
           ELSE,
           \ queue is empty...
               CARD @@ R12 MOV,      \ make sure to select the card
               5 SBZ,                \ set -CTS line LOW to get more data
           ENDIF, 
           NEXT,
    ENDCODE
    
    \ **************************************************************
    \ * ISR is in workspace 83C0. ONLY R3 & R4 are free to use!!!
    DECIMAL
    CREATE TTY1-ISR ( *isr with hardware handshake * )
           ISRWKSP LWPI,                                           \   10
           R12 CLR,          \ select 9901 chip CRU address        \   10
           2 SBZ,            \ Disable VDP int prioritization      \   12
           R11 SETO,         \ 3.5.16 hinder screen timeout        \   10 
           R12 COM1 LI,      \ select card1+uart1                  \   12 
           QTAIL @@ R4 MOV,  \ Queue tail pointer ->R4             \   22
           16 TB,            \ interrupt received?                 \   12
           EQ IF,            \ Yes; enqueue char                   \   10
                Q (R4) 8 STCR,  \ read byte into Q                 \   52
            \  *** manage Queue pointer ***
                R4 INC,            \ bump the index                    10
                R4 QMASK ANDI,     \ wrap the index                    14
                R4 QTAIL @@ MOV,   \ save index in QTAIL               22
    
            \  *** test buffer status ***
                QHEAD @@ R4 SUB,   \ R4 has Qtail                      22
                R4 ABS,            \ R4 has byte count in Q            12
                R4 QSIZE 2/ CI,    \ 1/2 full?                         14 
                GTE IF,                                             \  10
                \ we can change CTS line by using a negative bit value
                   -27 SBO,          \ CTS line HIGH. I am busy!       12
                ENDIF,
           ENDIF,
           18 SBO,         \ clr rcv buffer, enable interrupts         12
           R12 CLR,        \ select 9901 chip CRU address              10
           3 SBO,          \ reset timer int                           12
           RTWP,           \ Return                                    14
                           \          104.6 uS                        314 
    
    \ *******************************************************************
    \ * Configure ROM ISR to pass through external interrupts as VDP interrupts
    \ *   (Jeff Brown/Thierry)
    
    HEX
    \ get address Forth's tos register (R4) so we can transfer ISR handler 
    \ to the ISR workspace 
    8300 4 CELLS + CONSTANT 'TOS
    
    CODE INSTALL ( ISR_address -- )
           0 LIMI,
           83E0 LWPI,       \ select GPL workspace 
           R14 CLR,         \ Disable cassette interrupt; protect 8379
           R15 877B LI,     \ disable VDPST reading; protect 837B
    
           ISRWKSP LWPI,    \ switch to ISR workspace
           R1 SETO,         \ [83C2] Disable all VDP interrupt processing
           'TOS @@ R2 MOV,   \ [83C4] set our interrupt vector from Forth R4
           R11 SETO,        \ Disable screen timeouts
    
           R12 CLR,         \ Set to 9901 CRU base
           BEGIN,
              2 TB,         \ check for VDP int
           NE UNTIL,        \ loop until <> 0
    
           1  SBO,          \ Enable external interrupt prioritization
           2  SBZ,          \ Disable VDP interrupt prioritization
           3  SBZ,          \ Disable Timer interrupt prioritization
    
           8300 LWPI,       \ return to the FORTH WS
           TOS POP,         \ refill stack cache register
           2 LIMI,          \ 3.2  [rs232 ints now serviced!]
           NEXT,            \ and return to Forth
    ENDCODE
    
    DECIMAL
    CODE ISRON ( uart -- )  \ * Turn on the 9902 interrupts
           0 LIMI,
           TOS R12 MOV,
           18 SBO,          \  Enable rs232 RCV int
           TOS POP,
           2 LIMI,
           NEXT,
    ENDCODE
    
    CODE ISROFF ( uart -- )  \ * Turn off the 9902 interrupts
           0 LIMI,
           TOS R12 MOV,      \ i.e., >1340
           18 SBZ,           \ Disable rs232 rcv int
           TOS POP,
           2 LIMI,
           NEXT,
    ENDCODE
    
    : ISR-I/O
          QCLEAR            \ reset Queue pointers, erase data
          KEY? DROP         \ clear any char from 9902
          COM1 ISROFF       \ just to be safe
          TTY1-ISR INSTALL
          ['] QKEY? >BODY  ['] KEY? !  \ patch KEY?' to read the queue
          COM1 ISRON ;
    
    CR .( Intalling ISR on port TTY1 ...)
    ISR-I/O 
    CR .( ISR recieve enabled)
    CR 

     

     

    • Like 3
×
×
  • Create New...