Jump to content
Lee Stewart

fbForth—TI Forth with File-based Block I/O [Post #1 UPDATED: 02/23/2021]

Recommended Posts

Post #1 has been updated with all of the files for the latest build, fbForth 2.0:12. I will update my website later this week.

 

I fixed a couple of bugs:

  • The trigonometric functions in the Floating Point Library returned erroneous results after I hooked the Forth ISR as the system default (in build 9, I believe),
  • DOT (used to plot a pixel in Bitmap mode) ignored half of the color palette you could set with DCOLOR .

The latest FBLOCKS has only cosmetic changes—mainly, new binary images for four of the menu options that depend on the build with which they were BSAVEd.

 

Let me know if you find any bugs or need assistance with anything.

 

...lee

  • Like 5
  • Thanks 1

Share this post


Link to post
Share on other sites

User defined stacks for FbForth

 

So after you found my 2-! bug everything worked fine.

So as a thank you here is a version that works on FbForth.

\ STACKS.FTH    for FbForth     B Fox 2019

\ primitives
: CELLS   1 SLA ;
: 2/      1 SRA ; 
: CELL+   2+ ;
: CELL-   2- ;
: [email protected] ( adr -- n1 n2 )   DUP 2+ @ SWAP @ ;

: LIFO: ( #items -- )  \ "last in first out" creates a stack
         <BUILDS
            CELLS DUP , ( # of bytes in the stack)
                  DUP , ( max items on stack )
            ALLOT   0 , ( safety cell ? )
         DOES>
;

: [email protected]   ( addr -- addr' n )  CELL+ DUP @ ;
: STACK-SIZE  ( 'stack -- n ) @ ;
: STACK-DEPTH ( stack-addr -- n ) [email protected] - 2/ ABS ;

  : 2+!  ( addr -- ) DUP @ 2+ SWAP ! ;
  : 2-!  ( addr -- ) DUP @ 2- SWAP ! ;

: PUSH  ( n stack-adr - )
        [email protected] CELL- DUP 0= ABORT" User stack full"
        OVER 2-! + ! ;

: POP   ( stack-adr -- n )
        DUP [email protected] = ABORT" User stack empty"
        [email protected] OVER 2+! + @ ;

\ test code ...................
DECIMAL
  10 LIFO: Q

  99 Q PUSH  100 Q PUSH   101 Q PUSH

   Q STACK-DEPTH .

  Q POP . Q POP . Q POP .
  Q POP

 

Edited by TheBF
Dupicate definition
  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
On 11/17/2019 at 12:14 AM, Lee Stewart said:

Post #1 has been updated with all of the files for the latest build, fbForth 2.0:12. I will update my website later this week.

 

I realize that I have done absolutely nothing to my website (see my signature below) since at least the middle of November—probably even further back than that! And—I probably won’t get to it for a while yet. but I will eventually get to it, I promise. I definitely do not want to disappoint all y’all champing at the bit to have up-to-date Forthy things to peruse there!

 

...lee

  • Like 4

Share this post


Link to post
Share on other sites

I am starting to look at the SAMS code in fbForth 2.0 to change it to work from whatever low value seems reasonable (256 KiB?) through 16 MiB (the maximum for the 74LS612 chip).

 

Also, I am toying with changing the SAMS flag to save the number of available banks (0 – >1000). 0 would, of course, indicate no SAMS card.

 

Any thoughts?

 

...lee

  • Thanks 1

Share this post


Link to post
Share on other sites

i will need to apprise myself better of your implementation to be truly useful, but I use HEX 10 as the minimum page just to avoid hitting anything in the lower 64K address space.

After that, one can use the initialization routine to detect the last available page in the card and fill in your SAMS flag with that.

That is something I did not do yet but with the 4Mbyte cards in existence now I guess I should.

 

 

  • Like 2

Share this post


Link to post
Share on other sites

In case it provides any ideas here is my Forth version.  It assumes that we only map SAMS into >3000...>3FFF.

Compared to the same method with PAGED written in Assembler it has ~45% speed penalty depending on how it's used.

I have factored out MAP so that I can quickly map in value only without the overhead of computing the virtual address.

 

Spoiler
\ SAMS CARD support for CAMEL99 Forth   May 2020  B Fox
\ 64K segmented memory model
\
HERE
DECIMAL
  24 USER 'R12  \ address of R12 in any Forth workspace
HEX
: SAMSCARD  ( -- ) 1E00 'R12 ! ;   \ select sams card

  3000 CONSTANT PMEM  \ paged memory block location
\ compute SAMS register based on PMEM address
PMEM 0B RSHIFT 4000 + CONSTANT SREG

   VARIABLE SEG        \ holds current 64K segment
   VARIABLE BANK#      \ current mapped bank
FF CONSTANT LASTPAGE   \ true for 1M SAMS card only

\ using machine code so we don't need the CRU library
HEX
\ *set the CRU address in 'R12 before using these words*
  CODE 0SBO  ( -- ) 1D00 ,  NEXT, ENDCODE
  CODE 0SBZ  ( -- ) 1E00 ,  NEXT, ENDCODE

  CODE 1SBO  ( -- ) 1D01 ,  NEXT, ENDCODE
  CODE 1SBZ  ( -- ) 1E01 ,  NEXT, ENDCODE

: SAMS-ON   ( --) SAMSCARD 1SBO ;  \ enable mapper
: SAMS-OFF  ( --) SAMSCARD 1SBZ ;  \ disable mapper

\ safely set the 64K segment that you want to use
: SEGMENT ( 1..F -- )
         DUP 01 10 WITHIN 0= ABORT" SAMS segment err"
         SEG ! ;  \ don't allow segment 0
 1 SEGMENT

\ * SAMSINI sets card to "power-up" condition
: SAMSINI
       SAMSCARD          \ select SAMS card CRU address
       0SBO              \ turn card on
       0                 \ 1st value
       4000 20           \ register address, # SAMS regs
       BOUNDS ( -- 4100 4000)
       DO
           DUP I !       \ I is reg. address
           I @ OVER <> ABORT" SAMS card err"
           0100 +        \ next value
       2 +LOOP
       0SBZ              \ turn off card
       DROP
;

: MAP ( page -- )     \ map a page at HEX 3000
       ><             \ swap bytes, bank# must be in left byte
      SAMSCARD 0SBO   \ turn on the card
      ( bank#) SREG ! \ store bank in SAMS register
      0SBZ            \ turn off card
;
HEX
: PAGED  ( addr -- addr')
         SEG @ 1000 UM/MOD  ( -- offset bank#)
         DUP BANK# @ =      \ are we using the same PAGE
         IF
             DROP           \ Yes! Drop bank# and get out
         ELSE
             DUP BANK# !    \ update bank# variable
             MAP
         THEN  PMEM +       \ then add offset to paged mem block
;
CR HERE SWAP - DECIMAL . .( bytes)

SAMSINI SAMS-ON
CR .( SAMS card activated)

 

 

Note: ><  is SWPB.

 

 

  • Like 1

Share this post


Link to post
Share on other sites
11 hours ago, TheBF said:

i will need to apprise myself better of your implementation to be truly useful, but I use HEX 10 as the minimum page just to avoid hitting anything in the lower 64K address space.

After that, one can use the initialization routine to detect the last available page in the card and fill in your SAMS flag with that.

That is something I did not do yet but with the 4Mbyte cards in existence now I guess I should.

 

I used what @Willsy used in TurboForth, which presumed the presence/absence of precisely 1 MiB SAMS:

  • Default mapping of SAMS pages >00F8 – >00FF to RAM >2000, >3000, >A000, >B000, >C000, >D000, >E000, >F000, respectively. This cannot work with a SAMS card less than 1 MiB because it unconditionally maps the top eight pages of a presumed 1 MiB card!
  • Test for presence of SAMS:
    • Write >994A to >E000.
    • Map SAMS page >0001 to >E000.
    • Compare contents of >E000 to written value of >994A. If values match, no SAMS because mapping did not work.
    • Restore default mapping of SAMS page >00FE to >E000, if SAMS found.
  • Because mapping is done per an early scheme of duplicating the LSB of the SAMS page number to the MSB, any SAMS card greater than 1 MiB will not work correctly. This is because the LSN (Leasty Significant Nybble) of the MSB is now necessary for specifying SAMS pages higher than >00FF (the highest page of 1 MiB SAMS).

Your prudent avoidance of the lowest 16 SAMS pages notwithstanding, I do not think I need to be quite so careful because SAMS initialization in fbForth 2.0 occurs in ROM before any support code gets copied to RAM. I am torn between defaulting SAMS pages 0 – 7 to expansion RAM and forcing page number correspondence, viz., page 2 to >2000, page 3 to >3000, page 10 (>A) to >A000, ..., page 15 (>F) to >F000. I am inclined to go with the latter for mnemonic reasons, even though it realistically sacrifices 32 KiB of available SAMS. I am still working on it.

 

...lee

Edited by Lee Stewart
Correction to antepenultimate sentence, “I am torn,,,”
  • Like 2

Share this post


Link to post
Share on other sites
49 minutes ago, Lee Stewart said:

 

I used what @Willsy used in TurboForth, which presumed the presence/absence of precisely 1 MiB SAMS:

  • Default mapping of SAMS pages >00F8 – >00FF to RAM >2000, >3000, >A000, >B000, >C000, >D000, >E000, >F000, respectively. This cannot work with a SAMS card less than 1 MiB because it unconditionally maps the top eight pages of a presumed 1 MiB card!
  • Test for presence of SAMS:
    • Write >994A to >E000.
    • Map SAMS page >0001 to >E000.
    • Compare contents of >E000 to written value of >994A. If values match, no SAMS because mapping did not work.
    • Restore default mapping of SAMS page >00FE to >E000, if SAMS found.
  • Because mapping is done per an early scheme of duplicating the LSB of the SAMS page number to the MSB, any SAMS card greater than 1 MiB will not work correctly. This is because the LSN (Leasty Significant Nybble) of the MSB is now necessary for specifying SAMS pages higher than >00FF (the highest page of 1 MiB SAMS).

Your prudent avoidance of the lowest 16 SAMS pages notwithstanding, I do not think I need to be quite so careful because SAMS initialization in fbForth 2.0 occurs in ROM before any support code gets copied to RAM. I am torn between defaulting SAMS pages 0 – 8 to expansion RAM and forcing page number correspondence, viz., page 2 to >2000, page 3 to >3000, page 10 (>A) to >A000, ..., page 15 (>F) to >F000. I am inclined to go with the latter for mnemonic reasons, even though it realistically sacrifices 32 KiB of available SAMS. I am still working on it.

 

...lee

I found the overhead for a generalize mapper to be more than I could tolerate. I was trying to make a virtual-memory to real-memory convertor. (although that was not clear to me when I began :) )

For me I need an application to make sense of it. The possibilities are endless otherwise.

I like that test. It's simple. 

I noticed the duplicate LSB/MSB in the code but then found that it wasn't needed (ie it seemed to work ok without it) so again for speed I didn't do it. What did I lose by not doing the duplicate entry?

 

Yes our systems are quite different in realization with FbForth in ROM.  It's more important to get it right with ROM. I am still playing around. :) 

 

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, TheBF said:

What did I lose by not doing the duplicate entry?

 

Nothing. I think the folks who hit upon doing it that way in the early days were fumbling around trying to discover how the mapper worked. The important thing is to get the actual page number in the word and then swap bytes before MOVing it to a SAMS register. Even though, before swapping bytes for SAMS ≤1 MiB, the MSB should actually be >00, it does not matter what is there because writing the MSB has no effect, i.e., the mapper is only connected to the low-order 8 address lines. For SAMS > 1 MiB, however, that byte absolutely must have the proper value (>00 – >0F) else all hell breaks loose. In this case, it is the MSN of the MSB that does not matter, e.g., >0345 should, after SWPB, map the same page as >F345 because the mapper is now connected to 4 more address lines (now, the low-order 12} for a maximum of 16 MiB SAMS.

 

...lee

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

OK. After consulting the SAMS docs I have, Thierry’s site and checking with @Ksarul, the powerup state of the SAMS card has SAMS pages >0002, >0003, >000A – >000F mapped to >2000, >3000, >A000 – >F000, respectively. Because that is the initial mapping I had settled on anyway, I was thinking that is how I would leave it, viz., no startup initialization, but I think I should still initialize SAMS in the startup code because BOOT does not invoke DSR powerup routines, as happens with power cycling, but it does reset the Terminal Input Buffer ( TIB ) and the stack base ( S0 ) to the default >FFA0, which implies a reset of SAMS to default values. COLD , on the other hand, resets most everything except S0 and TIB , so should not reset SAMS.

 

Whether I use code that explicitly repages SAMS defaults or simply turn mapping off and back on depends on what actually happens with the latter two operations. I know, from Thierry’s site, that turning mapping off (SBZ 1) reverts to powerup mappings, but what is the state of affairs when mapping is turned back on (SBO 1)? Is the previously set mapping back in effect or does turning mapping off actually reset the mapper registers? I guess the latter is implied, but I could not find it explicitly stated on Thierry’s site.

 

...lee

 

 

  • Like 1

Share this post


Link to post
Share on other sites
On 8/18/2020 at 1:42 AM, Lee Stewart said:

the mapper is only connected to the low-order 8 address lines.

Hmm, by low-order 8 address lines, you mean A0 - A8?

 

 

On 8/21/2020 at 12:34 AM, Lee Stewart said:

Is the previously set mapping back in effect or does turning mapping off actually reset the mapper registers?

Only direct manipulation of the registers should cause them to change.

 

Spoiler

go1.thumb.JPG.5161bd7c5dd2777e35e6de85cf0f3a5a.JPG

go2c.thumb.JPG.9c66121673d91893cf60025e1e05551f.JPG

 

sams3a.thumb.JPG.ce3c6b819996d35e2d7dc7d4ddd9befe.JPG

sams4a.thumb.JPG.6596c6ca439f7a3f3a61c18a8703096b.JPG

sams5ca.thumb.JPG.9f4f9d5c982fd46cb117e2937c099976.JPG

sams6ca.thumb.JPG.e97a1cb9b339a7deed7295a453e5c17f.JPG

 

 

 

 

 

Edited by HOME AUTOMATION
Clipped and commented the images, for better clarity.

Share this post


Link to post
Share on other sites
7 minutes ago, HOME AUTOMATION said:

Hmm, by low-order 8 address lines, you mean A0 - A8?

 

A0 – A7 or however the low-order lines are numbered. TI liked to number data lines backwards—I think address lines were ordered the other way round. The point is that, in a 1 MiB SAMS, any value in the MSB (indicating a page number greater than >00FF) is ignored. Of course, this page number must be written to the mapper register with the bytes swapped, viz., >FF00.

 

...lee

  • Like 1

Share this post


Link to post
Share on other sites
9 hours ago, Lee Stewart said:

OK. After consulting the SAMS docs I have, Thierry’s site and checking with @Ksarul, the powerup state of the SAMS card has SAMS pages >0002, >0003, >000A – >000F mapped to >2000, >3000, >A000 – >F000, respectively. Because that is the initial mapping I had settled on anyway, I was thinking that is how I would leave it, viz., no startup initialization, but I think I should still initialize SAMS in the startup code because BOOT does not invoke DSR powerup routines, as happens with power cycling, but it does reset the Terminal Input Buffer ( TIB ) and the stack base ( S0 ) to the default >FFA0, which implies a reset of SAMS to default values. COLD , on the other hand, resets most everything except S0 and TIB , so should not reset SAMS.

 

Whether I use code that explicitly repages SAMS defaults or simply turn mapping off and back on depends on what actually happens with the latter two operations. I know, from Thierry’s site, that turning mapping off (SBZ 1) reverts to powerup mappings, but what is the state of affairs when mapping is turned back on (SBO 1)? Is the previously set mapping back in effect or does turning mapping off actually reset the mapper registers? I guess the latter is implied, but I could not find it explicitly stated on Thierry’s site.

 

...lee

 

 

It gets a little cloudy to me who has responsibility for what in a little system like TI-99.

Are we assuming power-up status or returning from another program to main menu etc.?

 

That being said I should think any APP that is going to use the card should set it up as it requires and assume nothing.

So if FbForth is going to wakeup with SAMS in play it has to do it. 

 

If my FbForth APP is going to use SAMS then I could simply load a block that sets up everything at compile time.  That block could be part of the library provided by FbForth.

 

After I figured out how to turn the card on etc. I just used some Forth words to fill it and viewed results on the classic99 debugger in >2000... >3FFF RAM.

That way I could see what happens with the mapper on or off. :)  Everything just switches back to what it was before with with SBO 1/SBZ 1

 

I think if you make 'R12 a constant for your R12 you can run my Forth version with a few word changes to the simple code words. 

You could compile it and explore. That's what I love about Forth and hardware interfaces.

 

 

  • Like 3

Share this post


Link to post
Share on other sites

Woohoo! I now have converted to @ralphb’s xas99 Cross-Assembler for building fbForth 2.0. I have code working with 128 KiB – 16 MiB SAMS. There are still a few patches I need to implement to get the Assembler to work properly for me—two in the Assembler and one in the code.

 

There are a few things I want to change before I release the next build. Hopefully, I will have enough room in ROM. Room is pretty scarce, so it may not be possible without some optimizing—we’ll see.

 

...lee

  • Like 3
  • Thanks 2

Share this post


Link to post
Share on other sites

As indicated in my last post, I have finally moved from Cory Burr’s asm994a Assembler over to Ralph Benzinger’s (@ralphb’s) Python-based Assembler (see xdt99: New TI 99 cross-development tools available) for fbForth 2.0 builds, starting with fbForth 2.0:13. My journey to assembly success is detailed in the xdt99 thread starting with post#336.

 

Build 13 will include:

  • Elimination of the trailing terminator bit in Name Fields of Forth words, which will include modification of all code that uses it: TRAVERSE NFA PFA -FIND (FIND) ID. CREATE <BUILDS .... (See, especially, post#440 for a detailed discussion of my reasoning.)
  • Use of SAMS cards with capacities from 128 KiB to 32 MiB. 32 MiB is currently only available in Classic99, with a current, real-iron maximum of 16 MiB.
  • If there is enough ROM space:
    •  Improvements to COINC COINCXY COINCALL SPRDIST SPRDISTXY
    •  DSRLNK improvements.

...lee

  • Like 5

Share this post


Link to post
Share on other sites

Knocked most of the way through the first bullet. I have CREATE to change—then <BUILDS and VLIST to check for any necessary modifications. All the code changes except ID. netted only 16 additional bytes. ID. went down 74 bytes!!

 

...lee

  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites

CREATE is done. <BUILDS and VLIST are fine without change. Below is the current boot screen in Classic99. Note the “SAMS:” entry on the right side of the eighth line.

 

fbForth2013BootScr.thumb.png.d8bd7f3828c215023868c88da4593ffc.png

 

...lee

  • Like 6
  • Thanks 1

Share this post


Link to post
Share on other sites

I am not sure there is much I can do to improve the following code for COINC COINCXY COINCALL SPRDIST SPRDISTXY except for removing the JMP between _COIN and _COINC:

Spoiler
; BLF2A  (routine in low RAM) BL from Forth in Bank 0 to ALC body in another bank
; RTNEXT (routine in low RAM) return to bank 0 and the Forth inner interpreter
*
* Bank codes for BLF2A routine
*
BANK0  EQU  >C000    
BANK1  EQU  >8000
BANK2  EQU  >4000
BANK3  EQU  >0000
*
* Register names specific to Inner Interpreter
*
U      EQU  8   <--points to base of User Variable area
SP     EQU  9   <--Parameter Stack Pointer
W      EQU  10  <--Inner Interpreter current Word Pointer
LINK   EQU  11  <--LINKage for subroutines in CODE routines
CRU    EQU  12  <--Used for CRU instructions
IP     EQU  13  <--Interpretive Pointer
R      EQU  14  <--Return Stack Pointer
NEXT   EQU  15  <--Points to the NEXT instruction fetch routine

; $SATR is the index into the User Variable area where the address of Sprite
; Atribute Table in VRAM is stored.

;[*-- _SPLOC --- popping sprite# to get SATR location of sprite data routine ***
*++          Call:  BL @_SPLOC
*++     Registers:  R0

_SPLOC MOV  *SP+,R0         ; pop sprite #
       SLA  R0,2            ; multiply by 4
       A    @$SATR(U),R0    ; get SATR VRAM address of this sprite's data
       RT                   ; return to caller
;]*
*++ body of SPRGET routine to allow call by other routines in this bank
__SPGT LIMI 0               ; disable interrupts because VSBR doesn't
       BLWP @VSBR           ; read sprite dy-1 to R1
       SRL  R1,8            ; move to right byte, clearing left byte
       INC  R1              ; increment dy
       AI   SP,-4           ; reserve 2 spots on stack
       MOV  R1,*SP          ; put dy on top
       INC  R0              ; point to dx
       BLWP @VSBR           ; read sprite dx to R1
       SRL  R1,8            ; move to right byte, clearing left byte
       MOV  R1,@2(SP)       ; put dx under dy
       RT                   ; return to caller without re-enabling interrupts
;[*** DXY ***        ( x1 y1 x2 y2 --- x^2 y^2 )
*        DATA SPGT_N
* DXY__N:
*        .name_field 3, 'DXY'
* DXY    DATA $+2
*        BL   @BLF2A
*        DATA _DXY->6000+BANK1
       
_DXY   BL   @__DXY          ; branch to body of this routine
       B    @RTNEXT         ; back to bank 0 and the inner interpreter

*++ body of DXY routine to allow call by other routines in this bank
__DXY  MOV  *SP+,R0         ; pop y2
       MOV  @2(SP),R1       ; get y1
       S    R1,R0           ; y2 - y1
*       ABS  R0              ; |y2-y1|
       MPY  R0,R0           ; |y2-y1|^2
       MOV  *SP+,R2         ; pop x2
       MOV  @2(SP),R3       ; get x1
       S    R3,R2           ; x2 - x1
*       ABS  R2              ; |x2-x1|
       MPY  R2,R2           ; |x2-x1|^2
       MOV  R3,@2(SP)       ; |x2-x1|^2 just below top of stack
       MOV  R1,*SP          ; |y2-y1|^2 to top of stack
       RT                   ; return to caller
;]   

;[*** SPRDIST ***    ( spr#1 spr#2 --- dist^2 | 7FFFh )
*        DATA HONK_N
* SDST_N:
*        .name_field 7, 'SPRDIST'
* SPRDST DATA $+2
*        BL   @BLF2A
*        DATA _SDST->6000+BANK1
       
_SDST  BL   @__SDST         ; branch to body of this routine
       B    @RTNEXT         ; back to bank 0 and the inner interpreter

*++ body of SPRDIST routine to allow call by other routines in this bank
__SDST MOV  LINK,R7         ; save return
       BL   @_SPLOC         ; get vaddr2 in R0
       MOV  R0,R5           ; save to R5
       BL   @_SPLOC         ; get vaddr1
*++ __SPGT disables interrupts for rest of this routine
       BL   @__SPGT         ; get dx1, dy1 to stack
       MOV  R5,R0           ; now for vaddr2
*++ from here on, same as SPRDISTXY
*++ __SPGT disables interrupts for rest of this routine
SDXYEN BL   @__SPGT         ; get dx2, dy2 to stack
       BL   @__DXY          ; pop dx1, dx2, dy1, dy2 and get dx^2, dy^2 to stack
       MOV  *SP,R3          ; get dy^2
       ANDI R3,>8000        ; highest bit = 1?
       JEQ  SDST03          ; no
SDST01 INCT SP              ; yes, reduce stack
SDST02 LI   R0,>7FFF        ; load highest positive integer
       JMP  SDSTEX          ; we're outta here!
SDST03 MOV  @2(SP),R3       ; get dx^2
       ANDI R3,>8000        ; highest bit = 1?
       JEQ  SDST04          ; no, add them
       JMP  SDST01          ; yes, finish up with >7FFF
SDST04 MOV  *SP+,R3         ; pop dy^2
       A    *SP,R3          ; add dx^2 to dy^2
       MOV  R3,R0           ; get ready to put result on stack
       ANDI R3,>8000        ; highest bit = 1?
       JNE  SDST02          ; yes, finish up with >7FFF
SDSTEX MOV  R0,*SP          ; result to stack
       B    *R7             ; return to caller
;]       
;[*** SPRDISTXY ***  ( dx dy spr# --- dist^2|7FFFh )
*        DATA SDST_N
* SDXY_N:
*        .name_field 9, 'SPRDISTXY'
* SPDXY  DATA $+2
*        BL   @BLF2A
*        DATA _SDXY->6000+BANK1
       
_SDXY  BL   @__SDXY         ; branch to body of this routine
       B    @RTNEXT         ; back to bank 0 and the inner interpreter

*++ body of SPRDISTXY routine to allow call by other routines in this bank
__SDXY MOV  LINK,R7         ; save return
       BL   @_SPLOC         ; get vaddr in R0
       JMP  SDXYEN          ; jump into SPRDIST routine to finish
;] 
;[*** COINC ***      ( spr#1 spr#2 tol  --- f ) ( 0= no coinc  -1= coinc )
*        DATA JOY__N
* COIN_N:
*        .name_field 5, 'COINC'
* COINC  DATA $+2
*        BL   @BLF2A
*        DATA _COIN->6000+BANK1
       
_COIN  LI   R7,__SDST       ; we'll be executing SPRDIST from _COINC
       JMP  _COINC          ; jump to coincidence routine

; We can eliminate above JMP to let this fall through to next instruction
; as long as we avoid inserting any instructions here!
;]                              
;[*-- _COINC --- coincidence routine ***
_COINC MOV  *SP+,R5         ; pop tol
       MPY  R5,R5           ; square it
       A    R6,R6           ; double it (2*tol^2)
       BL   *R7             ; get dist^2 from SPRDIST or SPRDISTXY
       S    *SP,R6          ; 2*tol^2 - dist^2
       JGT  COIN01          ; coincidence?
       CLR  *SP             ; nope, return 0
       JMP  COINEX          ; we're outta here
COIN01 SETO *SP             ; yup, return -1
COINEX B    @RTNEXT         ; back to bank 0 and the inner interpreter
;]
;[*** COINCXY ***    ( dx dy spr# tol --- f )
*        DATA COIN_N
* COXY_N:
*        .name_field 7, 'COINCXY'
* COINXY DATA $+2
*        BL   @BLF2A
*        DATA _COXY->6000+BANK1
       
_COXY  LI   R7,__SDXY       ; we'll be executing SPRDISTXY from _COINC
       JMP  _COINC          ; jump to coincidence routine
;]                           
;[*** COINCALL ***   ( --- f )       ( bit set if any two sprites overlap )
*        DATA COXY_N
* CNCL_N:
*        .name_field 8, 'COINCALL '
* CNCALL DATA $+2
*        BL   @BLF2A
*        DATA _CNCL->6000+BANK1
       
_CNCL  DECT SP                  ; reserve stack space
       MOVB @>837B,R0           ; copy VDP status byte to R0
       ANDI R0,>2000            ; check coincidence bit
       JNE  CNCL01              ; coincidence bit set?
       CLR  *SP                 ; no, clear flag
       JMP  CNCLEX              ; we're outta here
CNCL01 SETO *SP                 ; yes; set flag to -1
CNCLEX B    @RTNEXT             ; back to bank 0 and the inner interpreter
;]

 

As you can see in the above code, I am already extensively reusing code. I will look to see whether the code itself can be improved. Feel free to look over my shoulder. 😊

[EDIT: Please note that I added the code for DXY to the above spoiler. I had forgotten it—sorry.]

 

...lee

Edited by Lee Stewart
code addition
  • Like 1

Share this post


Link to post
Share on other sites

The only thing you might consider is writing these in CODE.  I kept DIST for computing actual distance but I felt it was too much overhead for coincidence since it's all just sitting there in VDP RAM to read and compare.

I think these could be really fast using registers versus the stack juggling in the Forth version.

 

Notice that I purposely have code duplication in COINC rather than calling COINCXY. This is just for a bit of extra speed.

CODE overhead with BL would be low enough to allow calling COINCXY IMHO.

 

: COINCXY   ( dx dy sp# tol -- ? )
        >R
        SP.Y [email protected] SPLIT
      ( -- col row col row )
        ROT - ABS [email protected] <
       -ROT - ABS R> <
        AND ;

: COINC ( spr#1 spr#2 tol -- ?)  \ 1.4 mS, 1.1 mS optimized
      >R  SP.Y [email protected] SPLIT
       ROT SP.Y [email protected] SPLIT
        ( -- col row  col row)
       ROT - ABS [email protected]  <
      -ROT - ABS R>  <
       AND ;

Just my 2 cents on the matter.

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, TheBF said:

The only thing you might consider is writing these in CODE.  I kept DIST for computing actual distance but I felt it was too much overhead for coincidence since it's all just sitting there in VDP RAM to read and compare.

 

Well, all of my code in my previous post is CODE, that is, it is pure ALC and the reuse is both via JMP and BL. I could not use JMP everywhere because of the need to return to some routines. I just need to take another look to see if there is any chance of paring it down.

 

...lee

Share this post


Link to post
Share on other sites

I am thinking that even in code the extra cycles required to compute distance to determine coincidence if pretty slow compared to subtracting the actual coordinates and comparing each difference to a tolerance value.

 

I have not got around to converting my Forth words to code but with a few temp registers it should be pretty efficient.

 

Share this post


Link to post
Share on other sites
35 minutes ago, TheBF said:

I am thinking that even in code the extra cycles required to compute distance to determine coincidence if pretty slow compared to subtracting the actual coordinates and comparing each difference to a tolerance value.

 

Now, you are starting the wheels turning.  For a tolerance of 1 or 2 pixels, differences work, but fail at 3 when both differences are 3. Perhaps a small table? Or?

 

...lee

Share this post


Link to post
Share on other sites

The Forth code I posted is used with a tolerance of 8 typically and it seems to work great even in Forth. 

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...