Jump to content

TheBF

+AtariAge Subscriber
  • Posts

    4,470
  • Joined

  • Last visited

Posts posted by TheBF

  1. 1 hour ago, Vorticon said:

    How can I detect and identify a keypress in assembly without resorting to DSRLNK?

    KSCAN in console ROM and works ok.

    I do something like this in Camel99 (but with RPN syntax so this is un tested.

     

    OLDR11 DATA 0
    
    KSCAN  LWPI >83E0           can't change WS with BLWP as R13-R15 are in use
           MOV  R11,@OLDR11     save GPL R11
           BL   @>000E          call keyboard scanning routine
           MOV  @OLDR11,R11     restore GPL R11
           LWPI WREGS
           
           MOVB @>837C,R1       Check flag for key pressed
           SLA  R1,3            Flag value is >20
           JNC  NOKEY           No key was pressed
           MOVB @>8375,R1       Get key code
           RT
    NOKEY  R1 CLR,
           RT

     

    • Thanks 1
  2. From Hacker news:

    Besides his contribution to language design, he authored one of the best puns ever. His last name is properly pronounced something like "Virt" but in the US everyone calls him by "Worth".

    That led him to quip, "In Europe I'm called by name, but in the US I'm called by value."

     

    😆

     

    • Like 8
  3. 20 minutes ago, jrhodes said:

    I love rhubarb, especially on pancakes!

    🥞

    Rhubarb pancake syrup:

    8 cups chopped rhubarb
    3 cups water
    2 cups sugar

    Start by adding the rhubarb and water to a pot and simmer. Use a fork to mash the rhubarb as it softens.
    Strain it with a sieve and return the rhubarb water back to the pot.
    Add the sugar, heating until it dissolves.
    Let the syrup cool completely before pouring it into jars or bottles for storing.

    I am now thinking about Rhubarb Maple Syrup. Pricey but could be tasty as well.

    • Like 2
  4. 7 hours ago, apersson850 said:

    Mixing Forth and assembly is easy, but then you have to hang by your feet from the ceiling to read the programs right.

    That's why they call me Batman. 🤣

    image.jpeg.df3435f03ccb1897ed05c23fb0233a16.jpeg

    • Haha 4
  5. 1 hour ago, retrodroid said:

     

           MOV  R0, R1                   ; Copy original value
           SRA  R0, 3                    ; Div by 8 
           SLA  R0, 3                    ; Mpy by 8
           C    R0, R1		
           JEQ  NOREM                    ; R0 is divisible evenly by 8, with no remainder 
    
    		

    vs

           LI	R2, 8               
           DIV  R2, R0                     ; Div by 8 
           JNO  NOREM				; No remainder

     

    ...but it seems It would be waaay more efficient to use the second version instead.

     

    The 9900 takes no prisoners.  It can be very hard to extract faster times from the old girl by using more instructions in my experience.

    Sometimes you win, but you have to test to be certain. 

     

     

    • Like 2
  6. In the VDP packed string arrays in the previous post, I changed ADD$ to give it protection from writing past your allocated TABLE size. 

    Much better. :) 

     

    : ADD$  ( addr len vdp[] size -- ) 
      2DUP + >R   \ Rpush end of array 
      SEEKFREE ( -- addr len Vaddr) 
      2DUP + R> > ABORT" Can't ADD$" 
      DUP>R VPLACE R> VALLOT ;   
    

     

    • Like 1
  7. Happy New Year Everybody :party:

     

    I was looking at some old code I built for HsForth based on the "Let's Build a Compiler" by Jack Crenshaw

    Let's Build a Compiler! (penguin.cz)

    It builds a tiny Pascal compiler and one day I may get enough energy to port it to TI-99.

     

    I wondered about putting the keyword table and the symbol table in VDP RAM.  You can cram a lot of text into memory

    using counted strings as a poor-mans linked list.  

     

    Somebody might want something like this for another purpose and it should port over to the other Forth systems with a few word replacements.

    If you hit a wall just ask. 

    VC!    -> VSBW
    VC@    -> VSBR
    VWRITE -> VMBW
    POSTPONE -> COMPILE

     

    I have a little VDP manager library that I include to begin

    \ VARIABLE VP    ( moved to kernel in V2.55 )
    
    HEX 1000 VP !   \ "VDP pointer" start of free VDP RAM
    : VHERE   ( -- addr) VP @ ;   \ FETCH the value in VDP pointer
    : VALLOT  ( n -- )   VP +! ;  \ add n to the value in VDP pointer
    : VC,     ( n -- )   VHERE VC!  1 VALLOT ;
    : V,      ( n -- )   VHERE V!   2 VALLOT ;
    : VCOUNT  ( vdp$adr -- vdpadr len ) DUP 1+ SWAP VC@ ;
    : VCREATE ( <text> -- ) VHERE CONSTANT  ; \ address when <text> invoked
    \ : VPLACE   ( $addr len Vaddr -- )  \ like PLACE for VDP RAM. In KERNEL 2.6
    \           2DUP VC! 1+ SWAP VWRITE ;
    

     

    Spoiler has the implementation:

    Edit: I added protection to ADD$ so it doesn't go past the allocated size of the array.

    Spoiler
    \ compact string tables in VDP RAM          Jan 2024 Brian Fox
    
    \ NEEDS DUMP   FROM DSK1.LOWTOOLS
    NEEDS VCOUNT FROM DSK1.VDPMEM
    
    \ "place" string caddr/u in VDP memory as byte-counted string
    : VS,  ( caddr u -- ) VHERE OVER 1+ VALLOT VPLACE ;
    
    \ compile a string into VDP memory
    : ,"       [CHAR] " PARSE  VS, ;
    
    : NEXT$   ( $addr -- $addr')  VCOUNT + ;
    : NTH$    ( $list n -- $addr)  0 ?DO  NEXT$  LOOP ; \ GOTO the nth string
    
    \ syntactic sugar. Get length of a string
    : VLEN  ( $addr -- ) POSTPONE VC@ ; IMMEDIATE 
    
    \ compile null string (0) to start list 
    : VDP{  ( -- VDPaddr) VHERE   0 VC, ; 
    
    \ compile 0 to end array
    : }VDP  ( Vaddr -- Vaddr size )
        0 VC,            \ end with a null string   
        VHERE OVER -  ;  \ compute the size in bytes 
    
    \ tables are recorded as a 2CONSTANT 
    : 2CONSTANT CREATE , ,  DOES> 2@ ;
    
    DECIMAL
    \ Neil Baud's COMPARE modified to compare RAM string to VDP string
    \  0 means adr1 = adr2
    \ -1 means adr1 < adr2
    \  1 means adr1 > adr2
    : VCOMPARE  ( addr u1 Vaddr u2 -- -1|0|1 )
        ROT  2DUP - >R            ( a1 a2 n2 n1) ( R: n2-n1)
        MIN                       ( a1 a2 n3)
        BOUNDS  ( loop index I becomes the VDP address)
        DO                        ( a1)
            COUNT  I VC@ -        ( a1 diff)
            DUP IF
                NIP 0< 1 OR       ( -1|1)
                UNLOOP
                R> DROP 
                EXIT
            THEN                  ( a1 diff)
            DROP                  ( a1)
        LOOP
        DROP                      ( )
        R> DUP IF  0> 1 OR  THEN  \ 2's complement arithmetic
    ;
    
    \ LOOKUP Returns index into the table or zero 
    : LOOKUP ( addr len table size -- ndx )
          DROP 
          NEXT$ -ROT   
          PAD PLACE 
          1 SWAP 
          BEGIN
            DUP VLEN
          WHILE ( string<>0)
            DUP VCOUNT PAD COUNT 2SWAP VCOMPARE 
          WHILE ( Vcompare<>0)
            NEXT$
            SWAP 1+ SWAP 
          REPEAT
          THEN
          ( -- ndx Vaddr )
          VLEN 0>      \ if string length=0 we hit the end 
          ( ndx ?) AND  \ and ndx with flag  
    ;
    
    \ create a table of strings that you can add to easily
    : TABLE: ( n -- )
      CREATE
        VHERE               \ VDP address of the table 
        SWAP DUP ,  VALLOT  \ record size, allocate VDP space
        ,
        0 VC,               \ compile null string in the table
      DOES> 2@   ( -- Vaddr size)
    ;
    
    : SEEKFREE ( vdp[] size-- Vaddr)      
      BOUNDS   ( -- last 1st )
      NEXT$    \ skip the first null 
      BEGIN 
        2DUP > 
      WHILE ( last > 1st)
        DUP VLEN 
      WHILE 
        NEXT$
      REPEAT 
      THEN 
      NIP 
    ;  
    
    : ADD$  ( addr len vdp[] size -- ) 
      2DUP + >R   \ Rpush end of array 
      SEEKFREE ( -- addr len Vaddr) 
      2DUP + R> > ABORT" Can't ADD$" 
      DUP>R VPLACE R> VALLOT ;   
    
    : NEW  ( Vaddr size -- ) 0 VFILL ; 
    \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

     

     

    And here is what it looks like when you use it.

    \                   >> TEST CODE <<
    \ reset the VDP memory pointer to your chosen first address
    HEX 1000 VP !
    
    \ create a finite size list of string constants
    VDP{
      ," IF"  ," ELSE"  ," ENDIF"
      ," WHILE"  ," ENDWHILE"
      ," DO"   ," ENDDO"
      ," LOOP" ," ENDLOOP"
      ," REPEAT" ," UNTIL"
      ," FOR" ," TO" ," ENDFOR"
      ," BREAK"
      ," READ" ," WRITE"
      ," VAR" ," END"
      ," PROCEDURE"
      ," PROGRAM"
    }VDP 2CONSTANT KEYWORDS
    
    S" REPEAT"  KEYWORDS LOOKUP .
    S" absent"  KEYWORDS LOOKUP .
    
    
    1024 TABLE: SYMBOLS   
    SYMBOLS NEW
    
    S" symbol1"  SYMBOLS ADD$
    S" symbol2"  SYMBOLS ADD$
    S" symbol3"  SYMBOLS ADD$
    S" symbol4"  SYMBOLS ADD$
    S" symbol5"  SYMBOLS ADD$
    S" symbol6"  SYMBOLS ADD$
    S" symbol7"  SYMBOLS ADD$
    S" symbol8"  SYMBOLS ADD$
    S" symbol9"  SYMBOLS ADD$
    S" symbol10"  SYMBOLS ADD$
    S" symbol11"  SYMBOLS ADD$
    S" symbol12"  SYMBOLS ADD$
    S" symbol13"  SYMBOLS ADD$
    S" symbol14"  SYMBOLS ADD$
    
    
    \ test code to view the tables 
    : VTYPE  ( Vaddr len --) BOUNDS ?DO I VC@ EMIT LOOP ;
    : VPRINT ( V$ -- ) VCOUNT CR VTYPE ; 
    
    : .TABLE ( table size -- )
      BOUNDS 
      NEXT$ 
      BEGIN 
        2DUP >
      WHILE 
        DUP VLEN
      WHILE 
        DUP VPRINT
        NEXT$ 
      REPEAT
      THEN 
      2DROP 
    ;
    

     

    • Like 2
  8. * Equates were very confusing.
    * VRAM vs CPU RAM (I had no idea they were separate).
    * The workspace pointer.
    * BLWP
    * Memory mapped devices, an the memory map in general.  Why were things were they are?
    * The notion of the `-R` flag to the assembler (I always wrote code using "R0", "R5", etc., and did not understand why assembly failed without the `-R` flag).

     

    This little list is like the outline of a course called:

     

    "Introduction to Computing on the TI-99"

    Chapter 1:  Essential Knowledge

     

    Maybe you could give a short paragraph on what the key learning was for you, on each item, over the next few weeks to help Roberto.  

    (not meaning to make work for you, but it's a really good list IMHO) 

  9. 19 minutes ago, HOME AUTOMATION said:

    Executing a RESET VECTOR, while the WS, is set to the GPLWS, will save your current WS, STATUS, AND PC, there, before taking the branch.

    Perhaps this matters to DSRs/hardware. :ponder:

    That makes sense.  I was trying to save as many bytes as possible and found these two instructions gave me a reliable reset but my needs are not very sophisticated.

  10. 16 minutes ago, dhe said:

    ABORT LIMI 2                ENABLE INTERRUPTS
               LWPI GPLWS       LOAD GPL WORKSPACE REGISTERS
               BLWP @>0000   BRANCH THROUGH THE VECTOR >0000

     

    This seems overly complicated. I would think just blwp @>0000 would always work ?

     

    Ya that seems odd. LWPI sets the workspace but then BLWP is going to change it to whatever is in the vector at >0000.

     

    I get out of Forth reliably with:

    CODE BYE   ( -- )
              83C4 @@ CLR,        \ clear interrupt vector
              0000 @@ BLWP,       \ ROM reset vector is at 0000
              ENDCODE


    Just twist your head a bit to see it in conventional notation. ;)

     

    It's important to clear the interrupt vector because it could be hooked to code that is still in memory when you exit and it would try to keep running every 16mS.

  11. 19 minutes ago, dhe said:

    One thing that has always given me pause in Molesworth is:

     

      STATUS EQU >837C GPL STATUS BYTE

         then before returning they do:

            CLR @STATUS

     

     So the status byte, is a byte.

     CLR operates on a word.

     

      Is it dangerous to clear a word worth of memory, when you are aiming for a byte, or does the byte after the status byte not matter?

    It would be dangerous if >837D had something important in it.  In the Editor/Assembler manual. >837D is described as the character at current screen position for the GPL interpreter.

    So it is safe until you use >837D for some purpose in your code. 

     

    • Thanks 1
  12. 14 minutes ago, retrodroid said:

    Modern assembly coding for the TI (or any retro platform I imagine) is sooo much nicer than it was back then with the ability to code in a modern IDE, on a modern computer, and test against multiple different emulators, etc.

    I have that book as well, purchased in the 1980s at some time. 

     

    Modern tools are much better for sure, but those of using Forth Assemblers back in the 1980s (or in 2024 :) ) could write and test Assembly language code interactively like it was BASIC.

    Now of course if you code a big error the machine crashed but the reboot process was pretty quick and you were back into the editor toute suite. 

    Typically the code routines were short then you concatenated them together with Forth to do more complicated things but it was pretty sweet compared to the edit/assemble/load/bomb/ process with E/A tools. 

     

     

  13. I found another place where Forth is discussed on Discord. 

    Someone mentioned tail call optimization so I showed how I did it with a word I called GOTO ( *IP IP MOV,)

    Another poster asked "Couldn't you use BRANCH?'"

     

    After a second I realized I just had to convert the absolute address that GOTO uses, into an offset that Camel Forth BRANCH uses.

    And when I tried it it made the optimization ~5% faster because BRANCH in Camel99 lives in scratch-pad RAM. :)

     

    \ tail call optimizing semicolon for Camel99 Forth  Nov 27 2022 Brian Fox
    
    DECIMAL 
    : CELL-   2- ;
    : PREVXT ( -- XT)  HERE CELL- @ ; \ fetch the XT of previous compiled word
    
    \ -; does not end with EXIT because it is branching directly to another
    \ list of tokens. That other list will end in EXIT or NEXT.
    : -;  ( -- ) \ programmer controlled
          PREVXT >BODY              \ get previous XT, compute PFA
         -2 ALLOT                   \ erase the previous XT
          POSTPONE BRANCH HERE - ,  \ compile BRANCH to the PFA
          POSTPONE [                \ turn off compiler
          REVEAL
          ?CSP
    ; IMMEDIATE
    
    : COLON?  ( xt -- ?) @  [ ' DOCOL @ ] LITERAL = ;
    
    VARIABLE TAILCALL  \ control tail call optimizizing with this variable
                       \ TAILCALL ON  turns optimizer on
    
    : ;   ( -- )
         TAILCALL @ 
         IF 
             PREVXT COLON?
             IF   POSTPONE -;
             ELSE POSTPONE ;
             THEN 
          ELSE 
             POSTPONE ; 
          THEN 
    ; IMMEDIATE
    
    

     

    • Like 2
  14. 4 hours ago, dhe said:

    I printed the manpage you create for the release.

     

    I use it as a guide to what is fair game! 😃

    Thank you for keeping me on track. 

    I have been away from the code for a while and since I am not a real VI user, that is the only document that contains what I tried to get working. :)

     

     

    • Like 1
  15. My mistake. I was adding commands faster than I realized.

     

    I have corrected d$ and will get to the paste function so it behaves as expected. 

    I have created so many functions that I don't always remember how to use them correctly. :(

     

     

     

  16. Just now, Appeelicious said:

    I think assembly will have my attention this year, but I'll keep forth in mind 😛

    Assembly language is an excellent first step before trying Forth.

    Forth is closer to a macro assembler than a "language"... that is until you extend it. 

    • Like 1
  17. There is no handler for d$ so that one doesn't surprise me. 

    I need to look up what it means. :)

    (I will never duplicate the entire command set. Not enough space in a stock console) 

     

×
×
  • Create New...