Jump to content

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

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 3

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

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

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

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.

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

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

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 3
Posted (edited)

By having a file called SYSTEM.CHARAC on the system disk (#4) at boot time. That is *SYSTEM.CHARAC in p-system lingo.

You can use the file OS:SYSTEM.CHARAC as a template, if you like. It's a simple memory image of the character definitions.

Edited by apersson850
  • Like 2
  • Thanks 1
  • 2 weeks later...

So I'm very motivated to get the TIPI disks working fully with the pcode card without having to remove the TI disk controller. With my current set up, TIPI is at >1000 and the disk controller is at >1100. Everything works fine and the system sees the 3 TIPI drives except the Editor which throws a Stack Overflow error when attempting to edit anything. From what I understand from @apersson850, the p-system is detecting both the TIPI and the real disk controller and creating an additional PCB in VDP RAM, which reduces the Editor's available RAM just enough to make it crash. The TOPMEM pointer at >2782 points to the highest VDP RAM location available and the PCB's are normally placed starting at that location and TOPMEM is adjusted down accordingly based on the length of the PCB.

Now, since I don't need the physical drives when using the TIPI, in theory I should be able to remove the PCB for the TI controller and restore TOPMEM to its original content and *theoretically* the Editor will have enough RAM to work.

So I need some additional info:

How are the PCB's structured under the p-system? 

If each drive as its own PCB, why does the system not natively recognize more than 3 drives? Essentially, with both the TIPI and the TI controller coexisting, shouldn't we see 6 drives when we run the Volume command from the Filer? Or does the system only access the first controller it sees?

I've not managed to get the p-system to automatically find more than one disk controller in the system.

I can use it with the physical drives and two RAMdisks, but then I inject the PCB for the RAMdisks manually. To avoid changing TOPMEM I put them in the sprite attribute table, since I normally don't use that anyway.

 

A PCB is a PAB with some added information. The p-system stores the CRU base address and the address to call to make access quicker. Every PCB for a physical disk drive is the same. The system just changes the drive number in the associated PAB when doing the call.

The p-system has tables with unit characteristics. They in turn point to tables with addresses for INIT, READ, WRITE and STATUS, as applicable. All disk units point to the same place, since they use the same controller. But the p-system knows that there will not be more than three disks, so it doesn't populate the entries for units #10, #11 and #12.

So for a CorComp controller which has four drives you simply copy the same value as is stored for #4 to the place where you have #10. Had the controller been able to also handle DSK5 and DSK6 you could have continued a bit further.

But the RAMdisks have a different DSR, so they need a different PCB and different pointers in the unit table.

 

I can show the structure of the PCB but it will have to wait until next week, when I'm back at home again.

 

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