Jump to content
IGNORED

Can you help me with ASM?


Sergioz82

Recommended Posts

57 minutes ago, Asmusr said:

What's the reason you're using 8x8 magnified sprites instead of 16x16 sprites?

Mainly because I didn't want to use memory, after all pacman is a yellow ball, I didn't think I needed more details (but giving it two shades of yellow for example could be nice).

 

Would it benefit the movement/eating management (basically collision detection) if I used a 16x16 sprite?

Link to comment
Share on other sites

It would benefit the resolution of the sprites. Are you using that much memory for other things that you can't afford to define the larger sprites?

 

Now you overlap sprites with the creatures in the "parking place" i the middle. Thus one disappear when the eating sprite moves by. Why are they parked as sprites? Are they going out on a hunt later?

  • Like 1
Link to comment
Share on other sites

8 minutes ago, apersson850 said:

It would benefit the resolution of the sprites. Are you using that much memory for other things that you can't afford to define the larger sprites?

 

Now you overlap sprites with the creatures in the "parking place" i the middle. Thus one disappear when the eating sprite moves by. Why are they parked as sprites? Are they going out on a hunt later?

I have in mind some special items pacman can use but I don't know if there will be enough memory to implement them. I was also thinking to mess with voice module so  I'm being cautious with space.

 

Yes, the ghosts in the middle will move. They can partially do it already but I commented the call to the movement routine to focus on pacman.

They disappear indeed, actually there are 4 parked there but two are not visible due to hw limitations. My next step is to take care of such issue. I was thinking about drawing one at a time while others stay outscreen. Hopefully they'll get a flickering effect that would do as ghostly transparency.

  • Like 1
Link to comment
Share on other sites

Here's the flickering effect I had in mind. 

For the moment the ghosts just bounce around, now I have to make them move as they should. 

Ideally I'd like a different behavior for each one, but maybe I'm asking too much from myself :)

 

Anyway, if you guys are interested I'll gladly share the complete code for ghost movement/drawing.

Meanwhile, the trick I used is to hide the ghost sprites at each game loop. 

I use a byte for a count of 4. Each loop only the ghost corresponding to count number is drawn on screen, the others are moved at row C0 and so on, in rotation.

 

I  can foresee an issue with this method: as the game loop gets loaded with instructions it will inevitably slow down so the flickering will slow down as well and it could become annoying to the eye.

However, the loop is actually slowed down with a count of 1000 so I should have some margins. 

 

 

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

This way the ghosts will always flicker - even when there is no vertical overlap. The more common way to handle this is to rotate the order of the ghosts in the sprite attribute list each frame. Then they will only flicker if there are more than 4 sprites on a line. You can also order all the eyes after all the bodies. Then it's only the eyes that will flicker most of the time.

  • Like 3
  • Thanks 2
Link to comment
Share on other sites

As a general comment, the way I always work with sprites now, which is different from when I started programming games for the TI, e.g. Titanium, is that I maintain a list of (logical) sprite data in RAM, which is not bound to hardware sprites at all.

 

The data stored for each sprite include things like position, velocity, size (if the logical sprites can be bigger than a hardware sprite), pattern(s), animation settings, color, whether the sprite is visible, and any other attributes required. Then, each frame or game loop, I rewrite the entire sprite attribute table in VDP RAM by going through the logical sprites list, generating data for hardware sprites (y, x, pattern, color, early-clock) as needed, stopping if the 32 sprite limit is reached, or maybe ending before that.

 

This makes it easy to create a flicker routine by traversing the logical sprites in a different order each frame, and it also facilitates things like animation, bigger sprites, sprite movement, adding and deleting sprites, having more the 32 logical sprites, etc. It can also be faster than using VSBW to change hardware sprite attributes directly. But my method is somewhat bound to having the 32K RAM available, because storing logical sprite data in VDP RAM would be a lot slower.

  • Like 5
  • Thanks 1
Link to comment
Share on other sites

@Asmusr

Thank youvery much for your aid.

 

12 hours ago, Asmusr said:

This way the ghosts will always flicker - even when there is no vertical overlap. The more common way to handle this is to rotate the order of the ghosts in the sprite attribute list each frame. Then they will only flicker if there are more than 4 sprites on a line. You can also order all the eyes after all the bodies. Then it's only the eyes that will flicker most of the time.

Ok, I'll use this method.
Please tell me if I got it right:

 

In RAM I have an attribute list in this order: Ghost 1,2,3 and 4 (to be more precise, it's Ghost 1 - Eyes 1, Ghost 2 - Eyes 2 and so on)

With your method each time I update VDP I rotate the order this way:

 

VDP attribute list at game loop 1: Ghost 1,2,3,4

VDP attribute list at game loop 2: Ghost 4,1,2,3

VDP attribute list at game loop 3: Ghost 3,4,1,2

VDP attribute list at game loop 4: Ghost 2,3,4,1

VDP attribute list at game loop 5: Ghost 1,2,3,4

 

and so on. Correct?  

 

About your second comment, I'll rewrite my VDP update system. At the moment I VMBW at both routines for pacman and for ghosts. Instead I'm going to BL to a single VDP update routine (which as you suggest will update the whole sprite attributes table at once) at the end of each loop after all the game logic calculations are done.

 

I had in mind that RAM and VDP data should have 1:1 correspondence but you shown me that VDP data are for screen drawing, they don't contribute to game logic calculations. 

 

  • Like 2
Link to comment
Share on other sites

1 hour ago, Sergioz82 said:

VDP attribute list at game loop 1: Ghost 1,2,3,4

VDP attribute list at game loop 2: Ghost 4,1,2,3

VDP attribute list at game loop 3: Ghost 3,4,1,2

VDP attribute list at game loop 4: Ghost 2,3,4,1

VDP attribute list at game loop 5: Ghost 1,2,3,4

 

and so on. Correct? 

Yep

Link to comment
Share on other sites

3 hours ago, Sergioz82 said:

... VDP data are for screen drawing, they don't contribute to game logic calculations.

That sounds like a very good conclusion. Optimize data for its purpose. The game logic is probably more complicated than a translation from logic data to VDP data format.

  • Like 2
Link to comment
Share on other sites

On 7/10/2022 at 8:43 AM, Asmusr said:

Yep

 

Here's my basic routine to do the trick.

I explain the idea behind it so hopefully it can be useful to other beginners like me:

 

I define a temporary attribute list in RAM. Each game loop I rotate the position of the sprites according to an offset that is incremented each game cycle up to "Last position of Temporary Attribute List - 1". Each position is 4 bytes long, corresponding to the attributes of a single sprite: 2 for sprites coordinates and 2 for pattern and color.

 

Each time I do a double MOV with autoincrement. MOV increments two, therefore first MOV copies coordinates and points to next word, now second MOV copies the pattern and the color. With second increment R0 and R1 point to next sprite in RAM A.T.

 

The offset works this way:

at first cycle it's 0. The routine just copies 1:1 of what's in my RAM attribute table into temporary table, let's say order of sprites is 1 2 3 4 so in temporay table it will be 1 2 3 4 too.

At second cycle it's 4 (4 bytes increment). Now it copies RAM data starting to second sprite entry in temporary table. If R1 goes over the end of the temporary table it's resetted back to position 1. Meanwhile there's still one sprite to copy from RAM A.T to Temp A.T but now it will be copied in position 1.

Now Temp A.T is  4 1 2 3

...

Let's skip to offset 12: 1 2 3 4 in RAM A.T is copied starting from position 4 in Temp. A.T, therefore now it's 2 3 4 1.

At this point, offset is cleared to 0 so next cycle it's back to 1:1 scenario, that is, 1 2 3 4 in Temp. A.T

 

It works, but obiviously not as well as it should.. the sprites glitch like crazy, in between correct frames I see random horizontal lines here and there.

I can't post a video now I'll do it later but I have pictures with corrupted sprites.

 

I used debugger to check what happens at >0300 in VDP and it seems the routine does its job.

Do you guys see some fault in the code? Maybe a broken increment/jump?

 

Quote

UPDVDP

       LI R0,ATTLST                  *Load Sprite attribute table start address (RAM)
       LI R1,SPSHFT                 *Load address of a shift area in RAM, allocated with BSS 40                                            
       MOV @SWPOFF,R3          *Load rotation offset                                                
       A R3,R1                         *Add offset to swap area                                         
                                                                                
SHIFT  MOV *R0+,*R1+        *Move word in *R0 to *R1, implicit +2 increment (move sprite coordinates word)                                                     MOV *R0+,*R1+          *Move word in *R0 to *R1, implicit +2 increment (move sprite pattern and color word) 
       CI R1,SPSHFT+40          *Is R1 over the last position of the shift area?                                               
       JLT GO1                        *No, keep incrementing                                        
       LI R1,SPSHFT                *Yes, point back to first position                                              
GO1    CI R0,ATTLST+40       *Is R0 over the last word in RAM attribute table?                                                   
       JLT SHIFT                      *No, keep moving                                          
                                                                                
       AI R3,4                         *Increment offset by 4 (4 bytes of sprite attributes)
       MOV R3,@SWPOFF        *Save the result for the next game loop                                                  
       CI R3,40                      * Is the offset over last position in swap area? 
       JLT WVDP                     *No, keep the value                                            
       CLR @SWPOFF              *Yes, no offset                                                
                                                                                
WVDP   LI R0,>0300             *Write the rotated sprite table in VDP sprite attributes table                                                 
       LI R1,SPSHFT                                                             
       LI R2,40                                                                 
       BLWP @VMBW                                                               
                                                                                
       B *R11                  

 

glitch1.bmp glitch2.bmp glitch3.bmp

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

Are you seeing simple 'tearing' - adjusting the sprites when they are actually being drawn?

One way to get around that would be to try and do it in the vertical blank period. That would depend on when the VDP interrupt fires I guess - beginning or end of frame etc.

Edited by Willsy
typo
  • Thanks 1
Link to comment
Share on other sites

4 hours ago, Sergioz82 said:

I'll try to figure out how to check and wait the vertical blank. Are there any topics on this forum?

 

Lots. A search for "VSYNC" should get you what you're looking for.  Here is what I've settled on:

VSYNC
       CLR R12               ; CRU Address bit 0002 - VDP INT
VWAIT  TB 2                  ; CRU bit 2 - VDP INT
       JEQ VWAIT             ; Loop until set
       MOVB @VDPSTA,R12      ; Clear interrupt flag manually since we polled CRU

The 9918A VDP sets an interrupt signal at the start of vertical blank (59.94 times per second) which is immediately after the 24th line of characters at the bottom of the visible portion of the screen, where it starts displaying solid border color.  The VDP interrupt is connected 9900 CPU interrupt, and also to the 9901 chip which is connected to the CRU signals on the CPU.  To read the interrupt from the 9901 we need to read CRU address 2, using the TB (test bit) instruction relative to the address in the R12 register.  The JEQ spins in a loop until the interrupt occurs, but then interrupt must be cleared by doing a read of the VDP status register.  Normally the ISR (interrupt service routine) would handle reading the status register after the interrupt, but I always run with the CPU interrupts off and the ISR disabled, by using "LIMI 0" at the start of my program.

Edited by PeteE
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

3 hours ago, mizapf said:

One could also use IDLE in this case, then check for the VDP INT.

Wouldn't that also allow the ISR to run?  In my programs, I avoid the ISR because I'm using as much of the scratchpad RAM as possible for my own use.

 

And the ISR changes the address register in the VDP, which if it occurs between your own code setting the address and writing the data, could very easily be causing the glitches described.

Edited by PeteE
  • Like 1
Link to comment
Share on other sites

2 hours ago, PeteE said:

Lots. A search for "VSYNC" should get you what you're looking for.  Here is what I've settled on:


VSYNC
       CLR R12               ; CRU Address bit 0002 - VDP INT
VWAIT  TB 2                  ; CRU bit 2 - VDP INT
       JEQ VWAIT             ; Loop until set
       MOVB @VDPSTA,R12      ; Clear interrupt flag manually since we polled CRU

The 9918A VDP sets an interrupt signal at the start of vertical blank (59.94 times per second) which is immediately after the 24th line of characters at the bottom of the visible portion of the screen, where it starts displaying solid border color.  The VDP interrupt is connected 9900 CPU interrupt, and also to the 9901 chip which is connected to the CRU signals on the CPU.  To read the interrupt from the 9901 we need to read CRU address 2, using the TB (test bit) instruction relative to the address in the R12 register.  The JEQ spins in a loop until the interrupt occurs, but then interrupt must be cleared by doing a read of the VDP status register.  Normally the ISR (interrupt service routine) would handle reading the status register after the interrupt, but I always run with the CPU interrupts off and the ISR disabled, by using "LIMI 0" at the start of my program.

Thanks @PeteE

This is very concise and going into my system's library. 

  • Like 1
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...