Jump to content
IGNORED

Pascal on the 99/4A


apersson850

Recommended Posts

Where is all the TI-centric p-system software? I checked out the Whtech site and all there is are the system disks. 

I'm looking for examples of what was achieved using that system aside from @apersson850's own work. Were there any games made for it using the TI libraries included with the system? The documentation for these can be spotty in places or missing important information (such as the sound volume detail discussed above), or advanced usage of sprites. 

For example, I am a bit unclear on the usage of the SPRITE_CHANGE_LIST. When I create a sprite using the SET_SPR_ATTRIBUTE function which is supposed to automatically create a SPRITE_CHANGE_LIST, how do I subsequently access specific fields for that sprite such as color or position since I don't know the name assigned to that list? So far the only way to do this that I know of is to use the SET_SPR_ATTRIBUTE function each time I want to make a change to the sprite, and that's a slow function. There has to be a better way.

  • Like 1
Link to comment
Share on other sites

4 hours ago, Tursi said:

I don't have a lot to contribute to Pascal discussion, but I do remember that 99er published a Pascal game called Cannibal. I never saw it run ;)

 

I'll see if I can find it. Thanks!

Link to comment
Share on other sites

22 hours ago, Vorticon said:

...and that's a slow function. There has to be a better way.

The advantages of separately compiled units are many, but speed isn't one of them. An inter-segment external global call is about the slowest thing you can execute as a p-code.

If you want to do a graphical game in Pascal for some reason, then you need to do something special with the graphics, I would presume.

Link to comment
Share on other sites

To answer my own questions about sprite usage, you can either set up a SPRITE_CHANGE_LIST manually or use SET_SPR_ATTRIBUTE, but not both because they will overwrite each other. The former is cumbersome because it's very verbose, but it's much faster than the latter once it is set up and provides you with additional features such as a sprite "lifespan countdown" and automatic linking to other lists, so it's a trade off. Below is an example. Link is already pre-defined by the SPRITE unit.

 

var
 car0 : link;

begin
(* define new sprite *)
 new(car0);

 car0^.packet := [spr_pattern..spr_x_vel];
 with car0^ do
  begin
   pattern_number := 136;
   color := 4;
   clock := 0;
   y_pos := 175;
   x_pos := 0;
   y_vel := -10;
   x_vel := 5;
   countdown := 0;
   link := nil;
  end;
(* start up the sprite *)
 set_sprite(0, car0);

(* change the sprite's position and velocity *)
 with car0^ do
  begin
   y_pos := 99;
   x_pos := 127;
   y_vel := 0;
   x_vel := 0;
  end;
(* with any change the sprite has to be reassigned to the SPRITE_CHANGE_LIST *)
 set_sprite(0, car0);

(* Now compare this to using just the SET_SPR_ATTRIBUTE function *)
(* Define sprite and set it in motion *)
 SET_SPR_ATTRIBUTE(136, 4, 0, 175, 0, -10, 5);

(* change sprite position and velocity *)
SET_SPR_ATTRIBUTE(136, 4, 0, 99, 127, 0, 0);

 

  • Like 2
Link to comment
Share on other sites

There is actually a way to get a pointer to a sprite's change list after using the SET_SPR_ATTRIBUTE function: simply use the GET_SPRITE function! Not sure how I missed that. In other words, set up a sprite with SET_SPR_ATTRIBUTE, then execute a GET_SPRITE to get a pointer to it. From there on one can access any of the sprite elements in the change list directly.

 

Unfortunately, it turns out that sprite functionality in the p-system is not as flexible as one might think at first glance. For example, you cannot check sprite coincidence between 2 specified sprites. The SPRITE_COINC is a boolean function that returns true when any sprite coincides with any other sprite, plus there is no way to check for coincidence between a sprite and a specific coordinate point, unlike XB. And to make matters worse, the x_pos and y_pos values are not updated automatically as the sprite moves. Not sure if this is a bug or intentional, but it's a major limitation (I think it's a bug). There are workarounds of course, but it makes sprite usage clumsy at best. Feels half baked in some sense.

 

 

  • Like 2
Link to comment
Share on other sites

14 minutes ago, Vorticon said:

There is actually a way to get a pointer to a sprite's change list after using the SET_SPR_ATTRIBUTE function: simply use the GET_SPRITE function! Not sure how I missed that. In other words, set up a sprite with SET_SPR_ATTRIBUTE, then execute a GET_SPRITE to get a pointer to it. From there on one can access any of the sprite elements in the change list directly.

 

Unfortunately, it turns out that sprite functionality in the p-system is not as flexible as one might think at first glance. For example, you cannot check sprite coincidence between 2 specified sprites. The SPRITE_COINC is a boolean function that returns true when any sprite coincides with any other sprite, plus there is no way to check for coincidence between a sprite and a specific coordinate point, unlike XB. And to make matters worse, the x_pos and y_pos values are not updated automatically as the sprite moves. Not sure if this is a bug or intentional, but it's a major limitation (I think it's a bug). There are workarounds of course, but it makes sprite usage clumsy at best. Feels half baked in some sense.

 

 

Pure conjecture here, but this sounds like the sprite table has a replica somewhere in RAM which is what you change in your program.

That table would be copied out to VDP RAM(maybe on the video interrupt?) to make things happen, but it seems they don't ever copy it back.

 

I played with that idea when I tried to make sprites work.  It was simpler to use the VDP memory functions to fetch and store bytes to/from VDP and just manipulate VDP RAM like it was normal memory.

It's slower but just as with P-code, you are not running at processor speed anyway so writing to RAM and doing a block copy to VDP didn't seem to by me much. 

 

Does Pascal give you functions to peek/poke VDP RAM?

 

@apersson850 can help us understand what is actually happening behind the curtain. 

 

 

 

  • Like 2
Link to comment
Share on other sites

I've never dechipered the sprite movement code in the p-system, since I've never used it for anything serious.

 

You can write peek and poke yourself in Pascal, thus also access VDP RAM directly. But if you do you have to make sure your program doesn't run from VDP RAM, since then you change the address to the running code.

  • Thanks 1
Link to comment
Share on other sites

I dug a little further into this and it turns out the Sprite Attribute List starts at >C00. This is interesting because according to the EA manual sprite auto-motion requires the SAL to be at >300, which tells me that the p-system seems to actively move sprites directly. Second, the table only contains sprites that are on screen, with those defined off-screen not represented. The table appears to be initialized to >C000 >0000 for each sprite otherwise. >C000 is 192,0 which is just off screen. And I can see that the x and y positions for the visible sprites are updated in real time.

So @TheBF's hunch is correct in that there must be a master copy of the SAL in RAM somewhere which is likely not a fixed location but depends on available memory for any particular program and the x and y positions in the VDP are not updating the RAM copy. This has got to be a bug. The good news is that I can easily write a small assembly procedure to check a sprite's position now that I know where to look for it. Homework for today 😁

  • Like 4
Link to comment
Share on other sites

35 minutes ago, Vorticon said:

I dug a little further into this and it turns out the Sprite Attribute List starts at >C00. This is interesting because according to the EA manual sprite auto-motion requires the SAL to be at >300, which tells me that the p-system seems to actively move sprites directly. Second, the table only contains sprites that are on screen, with those defined off-screen not represented. The table appears to be initialized to >C000 >0000 for each sprite otherwise. >C000 is 192,0 which is just off screen. And I can see that the x and y positions for the visible sprites are updated in real time.

So @TheBF's hunch is correct in that there must be a master copy of the SAL in RAM somewhere which is likely not a fixed location but depends on available memory for any particular program and the x and y positions in the VDP are not updating the RAM copy. This has got to be a bug. The good news is that I can easily write a small assembly procedure to check a sprite's position now that I know where to look for it. Homework for today 😁

It sounds like it works the same way as in all my games. Why do you think that's a bug? 

Link to comment
Share on other sites

46 minutes ago, Asmusr said:

It sounds like it works the same way as in all my games. Why do you think that's a bug? 

The only way to obtain the current sprite position is by looking up the x_pos and y_pos elements of the sprite's change list as there is no function to do that for you in the p-system. These elements are not getting updated as the sprite moves, and retain their original value when the sprite was created. This makes little sense and severely limits the usability of sprites. Yes, you can get around that by using assembly routines, but I'm talking here about what's available to the user within the p-system proper.

Link to comment
Share on other sites

56 minutes ago, Vorticon said:

The only way to obtain the current sprite position is by looking up the x_pos and y_pos elements of the sprite's change list as there is no function to do that for you in the p-system. These elements are not getting updated as the sprite moves, and retain their original value when the sprite was created. This makes little sense and severely limits the usability of sprites. Yes, you can get around that by using assembly routines, but I'm talking here about what's available to the user within the p-system proper.

I don't know much about the p-system, but it makes sense to me to keep your master list of sprites in CPU RAM and rebuild the sprite attribute list in VDP RAM on the fly. But if you can't read back the content of the master list, then yes, that's a problem. 

  • Like 3
Link to comment
Share on other sites

This may be of some interest @Vorticon

 

I build a thingy I call a table creators.  It allows me to point to a base address of any kind of memory and when given a parameter it computes the offset into that "table" using Assembly Language.

For the Sprite table I have TABLE4:  which assumes records are four bytes.  The offset into each sprite record takes only 2 instructions.   SLA Rx,2 and A Rx,Ry

 

I create a separate TABLE4 for each field in a SPRITE record by making each TABLE4 start one byte higher. 

 

Using this kind of thing means that reading and writing single bytes with a fetch and store operator into VDP RAM is still pretty quick. 

 

It ends up looking like this in Forth, using SAT (sprite attribute table) base address in VDP RAM.

SAT     TABLE4: SP.Y
SAT 1+  TABLE4: SP.X
SAT 2+  TABLE4: SP.PAT
SAT 3 + TABLE4: SP.COLR

 

The usage is like this

#1 SP.Y VC@    #1 SP.X VC@  \ "fetch sprite 1 x and y from VDP RAM

RED #3 SP.COLR VC!          \ set #3 sprite's colour

 

Not sure how that would translate to Pascal but the concept could have the same advantages as in Forth where there is an underlying interpreter slowing things down. 

 

 

 

  • Like 2
Link to comment
Share on other sites

Posted (edited)

Yes, Pascal runs a sprite movement system all by its own. I've never studied it in detail, as I've not used it. The only two things I remember is that it has two bitmaps at 280AH for sprites 0-15 and 16-31 where it indicates which are moving automatically. So when using Pascal, make sure your moving sprites are in one of these groups if you have no more than 16, since it will skip messing with the whole group if no sprite is moving there.

The other thing I remember is that the printout of the assembly routine handling the sprites is about two pages (A4). About 140 bytes of memory fit on one page.

As you understand I know where it is in memory, so I can easily find it and analyze it. Time permitting...

 

At 2808H there's a sprite coincidence counter, so some sprite related data is in that area.

Edited by apersson850
  • Like 2
Link to comment
Share on other sites

Here's a real life code snippet on how to check the position of a moving sprite. It uses VSBR (VDP Single Byte Read) from my VDPUTIL library. In testing I had this a repeat/until loop checking for a destination position and it worked really well and was reasonably fast.

 

set_sprite(sprnum, car[sprnum]);  (* start sprite moving *)
salloc := 3072 + (sprnum * 4); (* find location of sprite entry in the Sprite Attribute List *)
vsbr(salloc, y); (* get y position *)
vsbr(salloc + 1, x); (* get x position *)

 

Below is the listing of VDPUTIL ready to assemble.

 

Spoiler
;vdp access routines
;by walid maalouli
;march 2024

vdpwa   .equ    8c02h           ;vdp write address
vdprd   .equ    8800h           ;vdp read data
vdpwd   .equ    8c00h           ;vdp write data
vrtab   .equ    2810h           ;location of vdp registers copies for PME

        .proc   vwtr,2
;vdp write to register
;usage: vwtr(regnum, byteval)

        .def    vsavtab
        mov     *r10+,r0        ;get the register value
        li      r1,8000h        ;bit mask for msb
        swpb    *r10            ;r10 has pointer to register number
        socb    r1,*r10         ;set the most significant bit
        swpb    r0
        movb    r0,@vdpwa       ;write byte value
        nop
        movb    *r10,@vdpwa     ;write the register number
        nop
        b       *r11
        
vsavtab .block  8
        
        .proc   vsbr,2
;vdp single byte read
;usage: vsbr(vdpadr, intvar)
        
        mov     *r10+,r0        ;get the pointer to the variable
        swpb    *r10            ;r10 now has the vdp address
        movb    *r10,@vdpwa     ;write the vdp address lsb
        swpb    *r10
        movb    *r10,@vdpwa     ;write the vdp address msb
        nop
        clr     *r0
        movb    @vdprd,*r0      ;store vdp byte into variable
        swpb    *r0
        b       *r11
        
        .proc   vmbr,3
;vdp multiple byte read
;usage: vmbr(vdpadr, bytearray, bytenum)
        
        mov     *r10+,r0        ;get the number of bytes to read
        mov     *r10+,r1        ;get the pointer to the byte array
        swpb    *r10            ;r10 now has the vdp address
        movb    *r10,@vdpwa     ;write the vdp address lsb
        swpb    *r10
        movb    *r10,@vdpwa     ;write the vdp address msb
        nop
readnxt movb    @vdprd,*r1+     ;read byte
        dec     r0
        jne     readnxt         ;done reading all bytes?
        b       *r11
        
        .proc   vsbw,2
;vdp single byte write
;usage: vsbw(vdpadr, intval)

        mov     *r10+,r0        ;get the pointer to the variable
        swpb    *r10            ;r10 now has the vdp address
        movb    *r10,@vdpwa     ;write the vdp address lsb
        swpb    *r10
        li      r1,4000h        ;bit mask for vdp address msb
        socb    r1,*r10         ;set the 2 most significant bits
        movb    *r10,@vdpwa     ;write the vdp address msb
        swpb    r0
        movb    r0,@vdpwd       ;write byte to vdp address
        b       *r11
        
        .proc   vmbw,3
;vdp multiple byte write
;usage: vmbw(vdpadr, bytearray, bytenum)
        
        mov     *r10+,r0        ;get the number of bytes to write
        mov     *r10+,r1        ;get the pointer to the byte array
        swpb    *r10            ;r10 now has the vdp address
        movb    *r10,@vdpwa     ;write the vdp address lsb
        swpb    *r10
        li      r2,4000h        ;bit mask for vdp address msb
        socb    r2,*r10         ;set the 2 most significant bits
        movb    *r10,@vdpwa     ;write the vdp address msb
wrinxt  movb    *r1+,@vdpwd     ;write byte to vdp
        dec     r0              
        jne     wrinxt          ;done writing all the bytes?
        b       *r11
        
        .proc   regcopy,2
;save copy of vdp register in pme table at >2810
;usage: regcopy(regnum, byteval)
        
        mov     *r10+,r0        ;get register value
        mov     *r10,r1         ;get register number
        dec     r1              ;calculate vr address in table
        sla     r1,1
        ai      r1,vrtab
        movb    r0,*r1          ;save vr value in table
        b       *r11
        
        .proc   savevr
;save default pme vdp registers
;usage: savevr

        .ref    vsavtab
        li      r1,vrtab        ;point to pme vr table
        li      r2,7            ;number of registers to save
        li      r3,vsavtab      ;point to register save table
savnxt  movb    *r1+,*r3+       ;save register
        dec     r2      
        jne     savnxt          ;are all registers saved? (vr1-vr7)
        b       *r11
        
        .proc   restorevr
;restore default pme vdp registers
;usage: restorevr

        .ref    vsavtab
        li      r1,vsavtab      ;point to register save table
        li      r2,7            ;number of registers to restore
        li      r3,vrtab        ;point to pme vr table
resnxt  movb    *r1+,*r3+       ;restore register
        dec     r2
        jne     resnxt          ;are all registers restored? (vr1-vr7)
        b       *r11
        
        .end
        
        

 

 

  • Like 2
Link to comment
Share on other sites

1 hour ago, apersson850 said:

The utility modrs232 shows how you can access VDP RAM directly from Pascal, if you want to.

Indeed, but it's not complete. I preferred to roll my own to suit my needs better.

Link to comment
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.
Note: Your post will require moderator approval before it will be visible.

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