Jump to content

Sprite and Sound APIs


rje
 Share

Recommended Posts

I complain about being a bad assembly programmer, and perhaps most of that is just because I don't practice writing it.

So ok.  

I also complain about the USABILITY of the sprite and sound registers on VERA.

OK, it sounds like I have an intersection of interests here.

 

Does it makes sense to write a sound-and-sprite API?  I mean, is it a good place to dig into learning how to be a better assembly language programmer?  Maybe I can write something that hooks into cc65?

Looking for ideas and encouragement here.

Edited by rje
Link to comment
Share on other sites

Posted (edited)

Hey, I hadn't thought of it that way.  And I'm not sure that's true... but that's an angle I hadn't considered.

I guess the sound does require management... but Dusan has already written the guts of that; I can steal his code and adapt it.

I think of the assembly that must underpin Martin Kees' SuperBASIC.  SuperBASIC Documentation.pdf

Maybe you're right, maybe the line is blurry.

But I'm lazy, so I just think of an API.

Edited by rje
Link to comment
Share on other sites

Posted (edited)

SPRITE_API:  a jump table in available RAM.  In the $400s or $8000s or whatever.

SPRITE_API+$00: define sprite.   R0a = sprite number. R0b = sprite resolution. R1 = VERA RAM address or offset. R2a = width. R2b = height.
SPRITE_API+$03: move sprite.    R0a = number, R1 = x location, R2 = y location.
SPRITE_API+$06: kill sprite.         R0a = number.
SPRITE_API+$09: enable sprite.  R0a = number.

 

This is JUST an API -- and a relatively simple one.  It wraps the POKEs to VERA, and that's about it.  Yet the usability gain in my mind is enormous.

Edited by rje
Link to comment
Share on other sites

Posted (edited)

SOUND_API:  a jump table somewhere in available RAM.

 

More ambitious.  At its core it's Dusan's interrupt code with storage for envelope configurations and voice state.


SOUND_API+$00: define sound.  R0a = voice. R1a = Attack.  R1b = Decay.  R2a = Sustain.  R2b = Release.
SOUND_API+$03: play sound.  R0a = voice.  R1 = wave.  R2 = frequency.  R3 = pulse width.  R4 = volume.

VOICE ENVELOPES (16 bytes). Which envelope each voice is using, if any.
VOICE STATES(16 bytes).  (Internal) state variable.  Current state ('A','D','S','R', or 0 for 'done') of voice.  Used by interrupt.
VOICE TARGETS(16 bytes).  (Internal) state variable.  Target value for voice.  Used by interrupt.
ENVELOPE DATA(16 bytes).  Envelope configurations.  Four bytes per envelope: one each for Attack, Decay, Sustain, and Release, in jiffies or something.

Edited by rje
Link to comment
Share on other sites

I've always found that, more than necessity,  'simple annoyance' is actually the mother of invention.    So I say go for it!    There's no question that VERA and SOUND are tough (or at least tedious)  nuts to crack right now.   (I kinda feel nostalgic about that, since that was how the C64 was ...  you had to really grind to learn the VICII and SID registers and stuff before you could make magic!)

Speaking of annoyance, I've been playing with doing a little port / adaptation of one of my favorite arcade games in X16 BASIC and PETSCII graphics just to prove the speed of the thing makes it possible.   (If you do make a sound API I will happily try to adapt it for calling from BASIC which will save me a lot of brain damage when I get to the 'sound' part of my coding.)   In any event, I've found the annoying and repeated need for a way to do a fast and compact "locate" for the text screen, preferably with a color specification.     Sure, one can achieve the result by  a combination of PRINT TAB(x) and a string array holding down arrows, i.e., Y$(y) -  (costly in memory and degraded performance as x and y coordinates get further to the right or bottom of the screen); or, more cheaply in terms of memory by doing a few pokes and using 'SYS' to call the kernal 'PLOT' routine (an official supported routine with a dedicated vector even on the x16 and everything!), but...   like I said it reached the point of actual annoyance. 

So,  now I'm thinking I'll revive a dubious talent from my misspent youth and actually try to package the routine as a BASIC 'wedge' for the X16.   This will require using some rom calls whose addresses might change between now and the official release, but it will at least be a fun exercise anyway in any event!  

 

Edited by Snickers11001001
Link to comment
Share on other sites

Posted (edited)
13 minutes ago, Snickers11001001 said:

I've always found that, more than necessity,  'simple annoyance' is actually the mother of invention.    So I say go for it!    There's no question that VERA and SOUND are tough (or at least tedious)  nuts to crack right now.   (I kinda feel nostalgic about that, since that was how the C64 was ...  you had to really grind to learn the VICII and SID registers and stuff before you could make magic!)

Thank you!

See Martin Kees' document, which I attached above.  It REALLY opened up access to Sprites and Sound on the 64, and made me a more productive BASIC coder ... without consuming any of the precious RAM I needed for sprites and code.

 

Quote

In any event, I've found the annoying and repeated need for a way to do a fast and compact "locate" for the text screen, preferably with a color specification.    ...

So,  now I'm thinking I'll revive a dubious talent from my misspent youth and actually try to package the routine as a BASIC 'wedge' for the X16.   This will require using some rom calls whose addresses might change between now and the official release, but it will at least be a fun exercise anyway in any event!  

Something in my mind is telling me that Michael's KERNAL mods includes this, but I'm not sure.

Edited by rje
Link to comment
Share on other sites

I've been working towards a sound API - I'm still going round and round as to certain format issues / performance issues / etc.

There's a very nascent version of it in my Flappy Bird game. The idea is to use a simple data stream format for the various chips, and some basic function calls to start/stop them. My biggest issue right now is getting a little bit more experienced in dealing with banked memory, as there's just no way around using HiRAM for music and PCM data. SFX streams are very short and fit just fine in LoRAM, so it's easy enough to define those in-line in C as #include'd const byte arrays... but songs tend to push the 20KB barrier (VGM-exported songs I'm messing with) on YM, and even more for PSG.

Plus, there's already a simple sound engine for BASIC posted in the downloads area. Have you checked that out yet?

 

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

3 minutes ago, rje said:

Something in my mind is telling me that Michael's KERNAL mods includes this, but I'm not sure.

I'll double check.   My original take on looking at the reference guide was that the cursor-locate was referring to a pixel cursor and the new API for putting a character was in reference to the graphical (GEOS style) characters that go to the bitmap instead of the regular BASIC text screen.   Part of me still wants to mess with a wedge as a way to parse expressions or command parameters inline in basic instead of 'poke,poke,poke,poke,sys" combos.  At the moment, I'm working with a stub routine that you call with 'SYS' and it grabs its parameters from the first three integer variables.  We'll see how it goes. 

Link to comment
Share on other sites

Posted (edited)

Of course, I'm an idiot for not checking the DOCUMENTATION.

https://github.com/commanderx16/x16-docs/blob/master/Commander X16 Programmer's Reference Guide.md#sprites

Sprites

$FEF0: sprite_set_image - set the image of a sprite

$FEF3: sprite_set_position - set the position of a sprite

 

Function Name: sprite_set_image
Purpose: Set the image of a sprite
Call address: $FEF0
Signature: bool sprite_set_image(byte number: .a, width: .x, height: .y, apply_mask: .c, word pixels: r0, word mask: r1, byte bpp: r2L);
Error returns: .C = 1 in case of error

Description: This function sets the image of a sprite. The number of the sprite is given in .A, The bits per pixel (bpp) in r2L, and the width and height in .X and .Y. The pixel data at r0 is interpreted accordingly and converted into the graphics hardware's native format. If the .C flag is set, the transparency mask pointed to by r1 is applied during the conversion. The function returns .C = 0 if converting the data was successful, and .C = 1 otherwise. Note that this does not change the visibility of the sprite.

Note: There are certain limitations on the possible values of width, height, bpp and apply_mask:

  • Width and height may not exceed the hardware's capabilities.
  • Legal values for bpp are 1, 4 and 8. If the hardware only supports lower depths, the image data is converted down.
  • apply_mask is only valid for 1 bpp data.

 

Function Name: sprite_set_position
Purpose: Set the position of a sprite or hide it.
Call address: $FEF3
Signature: void sprite_set_position(byte number: .a, word x: r0, word y: r1);
Error returns: None

Description: This function shows a given sprite (.A) at a certain position or hides it. The position is passed in r0 and r1. If the x position is negative (>$8000), the sprite will be hidden.

Edited by rje
Link to comment
Share on other sites

Okay, now I have to load the accumulator with a parametric value.

Not sure how to do this in C.  I can do constant-string assembly:

asm("LDA $F00");



...but I really really really really doubt I can do this:

char onTheFly[16];

onTheFly = "LDA $BAA";
...
asm( onTheFly );



So I can't set the accumulator or X or Y registers that way.

 

I know the SYS command will load A, X, Y, and Status.  But... that's BASIC.  Does that call a KERNEL routine, perhaps?  Hmmm perhaps.

Link to comment
Share on other sites

Oh.  Found it:

 

Function

Call a subroutine passing register values.

Header

6502.h

Declaration

void __fastcall__ _sys (struct regs* r);

Description

The function will call the subroutine at the address specified in the pc member of the passed regs structure. All registers and the CPU flags are set to the values given in the regs structure. On return from the subroutine, the new values of the registers and flags are stored back overwriting the old values.

 

Link to comment
Share on other sites

Posted (edited)

If I use the _sys() call, I can call his ABI directly.  All I need is the registers set correctly (that's the struct) and some of the pseudo-registers in zero page.  I think that's what I'll try.
 

r.pc = 0xfef0;       // sprite_set_image
r.a = 0;             // sprite 0
r.x = 32;            // width
r.y = 32;            // height
setR0( addr );       // a macro for something that sets 0x0002 and 0x0003
eightBitsPerPixel(); // a macro for something like asm("LDA #8"); asm("STA $0006"); 
_sys( r );

Description: This function sets the image of a sprite.
The number of the sprite is given in .A, The bits per pixel (bpp) in r2L, and the width and height in .X and .Y.
T
he pixel data at r0 is interpreted accordingly and converted into the graphics hardware's native format.
 

^ That last bit is interesting.  Does that mean the sprite data is in main RAM and not on VERA?  It should already be in VERA, shouldn't it?  I load the sprite data direct to VERA... I'll assume it's a VERA offset.

 

Edited by rje
Link to comment
Share on other sites

You can use C-space variables in asm() calls with cc65.

C identifiers are imported into assembly with a _ prepended to their names.

So you could do this:

char[12] onthefly
...
asm ("lda _onthefly")

This would be like &onthefly - I haven't tried things like asm("lda #<_onthefly") - not sure how sophisticated the asm() command is, but since it's part of the same software suite that is also the assembler, I imagine it's able to decipher that.... I used it in my IRQ handler

asm("jmp (_systemIRQ)"  where in C, systemIRQ holds the address from $314 at boot. I don't know where C put it, and don't care. 😉

 

 

  • Thanks 1
Link to comment
Share on other sites

On 7/23/2021 at 11:23 PM, rje said:

^ That last bit is interesting.  Does that mean the sprite data is in main RAM, and not on VERA?  It should already be in VERA, shouldn't it?  I load the sprite data direct to VERA... I'll assume it's a VERA offset.

Those functions were designed for the mouse sprite.  Its shape data is in the Kernal ROM.  sprite_set_image is used to copy it into VRAM.  sprite_set_position is used to move the mouse pointer during vertical blanking.

Edited by Greg King
Link to comment
Share on other sites

10 hours ago, Greg King said:

Those functions were designed for the mouse sprite.  Its shape data is in the Kernal ROM.  sprite_set_image is used to copy it into VRAM.  sprite_set_position is used to move the mouse pointer during vertical blanking.

Ohhhhhhhh.

OK!  So I will at least want to write a C library for VERA sprite stuff.  And so I can still think about one in assembly too.

  • Like 1
Link to comment
Share on other sites

On 7/23/2021 at 9:23 PM, rje said:

^ That last bit is interesting.  Does that mean the sprite data is in main RAM and not on VERA?  It should already be in VERA, shouldn't it?  I load the sprite data direct to VERA... I'll assume it's a VERA offset.

 

Yes, the sprite data should already be in VRAM.  The sprite attribute table at 1FC00 will point to the start location of the sprite data in VRAM.

I found @SlithyMatt's sprite tutorial on YouTube very helpful. 

  • Thanks 2
Link to comment
Share on other sites

Dumb question as you guys are talking sprites. 

Is there an offscreen offset?    What I mean is if I want to have a sprite sort of drive onto the screen from the left side, can I set the sprite to a particular location that's outside the display and sort of scoot it in?   

Sorry for the dumb question. 

Link to comment
Share on other sites

20 minutes ago, Snickers11001001 said:

Is there an offscreen offset?    What I mean is if I want to have a sprite sort of drive onto the screen from the left side, can I set the sprite to a particular location that's outside the display and sort of scoot it in?

The VERA will interpret positions beyond the right/bottom edge as two's complement signed numbers, allowing sprites to start beyond the left/top edge of the screen.

Link to comment
Share on other sites

Thanks!   Well, that's some extra maths I guess I need to work on, LOL. 

ETA:   That also seems to answer another question I had about the need for 10 bits to represent the vertical position given a 480 pixel high screen.   Thanks again! 

 

Edited by Snickers11001001
Link to comment
Share on other sites

Posted (edited)
7 hours ago, Ed Minchau said:

Yes, the sprite data should already be in VRAM.  The sprite attribute table at 1FC00 will point to the start location of the sprite data in VRAM.

I found @SlithyMatt's sprite tutorial on YouTube very helpful. 

Ah, does he use the KERNAL sprite routines?  (https://github.com/commanderx16/x16-docs/blob/master/Commander X16 Programmer's Reference Guide.md#function-name-sprite_set_image)

Edited by rje
Link to comment
Share on other sites

1 hour ago, rje said:

Ah, does he use the KERNAL sprite routines?

No, I just use direct manipulation of VRAM. The Kernal routines are limited, and not terribly useful unless you are doing something exactly like what they were written for, and the VRAM interface is really just as simple to use.

Here's the video: 

 

Link to comment
Share on other sites

12 hours ago, Snickers11001001 said:

Thanks!   Well, that's some extra maths I guess I need to work on, LOL. 

ETA:   That also seems to answer another question I had about the need for 10 bits to represent the vertical position given a 480 pixel high screen.   Thanks again! 

 

If you're using C, just use int16_t (signed) variables to hold the X and Y positions, and it works itself out.

Link to comment
Share on other sites

Posted (edited)
10 hours ago, SlithyMatt said:

No, I just use direct manipulation of VRAM. The Kernal routines are limited, and not terribly useful unless you are doing something exactly like what they were written for,

That's what I thought - and thanks for that about the Kernal routines.  THAT's what I was trying to find out.

 

Quote

and the VRAM interface is really just as simple to use.

There's a VRAM interface?  OK I'll look at your tutorial.

See, I was going on the register documentation for VERA... and that is a jolly pain to work with, at least in BASIC.  VPOKEs with unfriendly bit packing.

In C, at least I can encapsulate things into a library, and from there perhaps develop assembly language routines.

My BASIC code would resemble this:

POKE $9F29,($40 OR PEEK($9F29)) :REM ENABLE SPRITES
rem ship
s0 = $fc10
x = px + 164
y = py + 164
bk = 2
ls = int(rnd(1)*8) * 32
fl = 0
po = 0 :rem pallette offset

VPOKE $F, S0+0, %00000000+ls :REM LSB
VPOKE $F, S0+1, %10000000+bk :REM R...BBBB
VPOKE $F, S0+2, X AND 255    :REM X
VPOKE $F, S0+3, X  / 256     :REM X MSB
VPOKE $F, S0+4, Y AND 255    :REM Y
VPOKE $F, S0+5, Y  / 256     :REM Y MSB
VPOKE $F, S0+6, %00001100+FL :REM coll,ZZ,FLIP
VPOKE $F, S0+7, %10100000+PO :REM HHWW..PO
	
Edited by rje
Link to comment
Share on other sites

The VERA registers are the VRAM interface. The bit packing / shifting does get crazy. I just made some macros to convert between the various formats.

// Macros to convert VRAM addresses into Hi/Low Addr bytes in SPR regs.

#define SPRlo(a)    ((a)>>5  & 0xff)
#define SPRhi(a)    ((a)>>13 & 0x0f)
#define SPRadr(a)    (SPRlo(a) | SPRhi(a) << 8 )

Edited by ZeroByte
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.

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.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use