Jump to content
IGNORED

CC65 VBLANK and DLI


Yaron Nir

Recommended Posts

Hello all,

contineuing my jurney with CC65, and was wondering if there is a CC65 native code for atari to handle VBLANK and DLI?

 

to be accurate,

1. i was to draw or move on screen only after VBLANK has occured (is that the safest?)

2. i will need to change the colors on raster lines and for that i need DLI

 

 

thanks for all the guru helpers

 

cheers

Yaron

Link to comment
Share on other sites

A parameter-less C function (void func(void) ) is indistinguishable from an assembler call. If you set up a vbi() function and put it's address into the vbi pointers and do the other necessary settings for a VBI setup, your routine will be called each vblank. You do need to jump back correctly, and that requires an asm("RTI") and stuff, but its pretty easy to use the asm() functions in C.

 

You can hook dli's too, but I am not sure how you would save the regs you need without asm. See the below functions. Using the asm() stuff in C is pretty easy if all you do is save regs, set a few things, and return.

#define DLIST  *((unsigned int *)560)
#define LMS_MASK 0x40
#define DLI_MASK 0x80
#define JVB      0x41
#define JMP      0x01
#define SCAN_GR0 0x02
#define SCAN_GR4 0x04

unsigned char color_save;
unsigned char bloo=(7 << 4 )+1;
unsigned char noo=(7 << 4 )+15;
char dl[256];

void dli0( void )
{

asm("     PHA      "); //save a

//chain to next DLI
asm("     LDA #<_dli1");
asm("     STA $0200");
asm("     LDA #>_dli1");
asm("     STA $0201");

//save color
asm("     LDA $2c6       ");
asm("     STA _color_save");

//load up color
asm("     LDA #1    ");
    asm("     STA $D40A "); // wsync
    asm("     STA $D018 ");

//restore a
asm("     PLA      ");
asm("     RTI      ");
}

void dli1( void )
{

asm("     PHA      "); //save a

//load up color
asm("     LDA #3   ");
asm("     STA $D40A"); // wsync
asm("     STA $D018 ");

//chain to next DLI
asm("     LDA #<_dli2");
asm("     STA $0200");
asm("     LDA #>_dli2");
asm("     STA $0201");

//restore a
asm("     PLA      ");
asm("     RTI      ");
}

void dli2( void )
{

asm("     PHA      "); //save a

asm("     LDA #7   ");
asm("     STA $D40A"); // wsync
asm("     STA $D018 ");

//chain to next DLI
asm("     LDA #<_dli3");
asm("     STA $0200");
asm("     LDA #>_dli3");
asm("     STA $0201");

//restore a
asm("     PLA      ");
asm("     RTI      ");
}

void dli3( void )
{

asm("     PHA      "); //save a

//load up color
asm("     LDA _color_save");
asm("     STA $D40A "); // wsync
    asm("     STA $2c6  ");
asm("     STA $D018 ");

//chain to next DLI
asm("     LDA #<_dli0");
asm("     STA $0200");
asm("     LDA #>_dli0");
asm("     STA $0201");

//restore a
asm("     PLA      ");
asm("     RTI      ");
}

void setup_dli_chain( void )
{
  *(unsigned int  *)(0x0200)=&dli0;
  *(unsigned char *)(0xD40E)=*(unsigned char *)(0xD40E) | 0x80;
}

unsigned int next;

void build_dl( void )
{
int sl=0;

dl[sl++]=0x70;
dl[sl++]=0x70;
//dl[sl++]=0x70;
dl[sl++]=SCAN_GR0 | LMS_MASK;
dl[sl++]=(int)&screen1 & 0xFF;
dl[sl++]=((int)(&screen1) & 0xFF00) >>8;
dl[sl++]=0;

    dl[sl++]=SCAN_GR0;
    dl[sl++]=SCAN_GR0;
    //dl[sl+0]=SCAN_GR0;
    //dl[sl+1]=JMP;
//next=(unsigned int)&dl[sl+4];
//dl[sl+2]=( next & 0xFF );
//dl[sl+3]=( (next >> 8 ) & 0xFF );
//sl+=4;

dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0 | DLI_MASK;
dl[sl++]=SCAN_GR0 | DLI_MASK;
dl[sl++]=SCAN_GR0 | DLI_MASK;
dl[sl++]=SCAN_GR0 | DLI_MASK;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
dl[sl++]=SCAN_GR0;
    dl[sl++]=JVB;
dl[sl++]=(int)&dl & 0xFF;           ;
dl[sl++]=((int)(&dl) & 0xFF00) >>8; ;
}


void activate_dls ( void )
{
  //set screen
  *(unsigned int *)(88)=&screen1;
  //kill antic by storing 0 into $22f
  *(unsigned char *)(0x22F)=0;
  //Then store the address of the new display list into $230 and $231 (low then high).
  DLIST=&dl;
  //Lastly, turn ANTIC back on with a $22 into SDMCTL.
  *(unsigned char *)(0x22F)=0x22;
}
Edited by danwinslow
  • Like 1
Link to comment
Share on other sites

... but I am not sure how you would save the regs you need without asm. See the below functions. Using the asm() stuff in C is pretty easy if all you do is save regs, set a few things, and return.

If one saves all the registers (AXYP) and restore them at the end (both with asm()), can one execute C code in between? (like update a global variable)

Link to comment
Share on other sites

ok. thanks for all responses, i think i understand.

 

a differnt question:

 

if i use this method:

int isVBLANK()
{
          // get the currect raster line
          raster_line = PEEK(54283);


          // if it reaches line 120 = VBLANK
          distance = abs(120-raster_line);
          return (distance == 0);
}

if i use the number 120, everything seems to work ok. if i choose bigger number, things start to mess up......

any idea? any suggestions for improvements?

 

Link to comment
Share on other sites

If one saves all the registers (AXYP) and restore them at the end (both with asm()), can one execute C code in between? (like update a global variable)

Yep, sure can. Again, probably stay away from c lib stuff, but any regular c stuff would be fine, as long as time constraints are paid attention to.

 

*edit*

P I don't normally worry about, but I guess you could.

Edited by danwinslow
Link to comment
Share on other sites

 

ok. thanks for all responses, i think i understand.

 

a differnt question:

 

if i use this method:

int isVBLANK()
{
          // get the currect raster line
          raster_line = PEEK(54283);


          // if it reaches line 120 = VBLANK
          distance = abs(120-raster_line);
          return (distance == 0);
}

if i use the number 120, everything seems to work ok. if i choose bigger number, things start to mess up......

any idea? any suggestions for improvements?

 

 

 

Yaron -

Why are you trying to wait until vblank is finished?

Link to comment
Share on other sites

I assume you know VBLANK is scanline/2. On an NTSC system it doesn't go beyond 130 (262 lines, start counting at 0, i.e. 0-130 equals 131 values) and on a PAL system that's 155 (312 lines). I suppose the C code takes too long to execute. That is, calling this function, doing the math, returning and evaluating the return value.

 

Edit: assuming PEEK is a macro, you are also calling the abs() function which slows it down even further.

Edited by ivop
Link to comment
Share on other sites

I assume you know VBLANK is scanline/2. On an NTSC system it doesn't go beyond 130 (262 lines, start counting at 0, i.e. 0-130 equals 131 values) and on a PAL system that's 155 (312 lines). I suppose the C code takes too long to execute. That is, calling this function, doing the math, returning and evaluating the return value.

 

Edit: assuming PEEK is a macro, you are also calling the abs() function which slows it down even further.

what could you suggest as aleternative to the code above. keep it C please.....

Link to comment
Share on other sites

what could you suggest as aleternative to the code above. keep it C please.....

 

Well, you only compate the result to 0. So abs() is not needed.

 

I don't understand your "keep it C please" if you accept danwinslow's solution. To me, this doesn't count as "kept to C" with all the inline assembly. What's your goal?

Link to comment
Share on other sites

 

well, that is the safest way to draw things on screen without messing things up right?

 

As far as I know, you don't have to wait for vblank to be over. You just draw your stuff, you'll be interrupted at some point but you won't know it and your routine will pick back up where it left off. The VBI won't mess with your graphics at all. It just increments some system values and does other housekeeping.

Link to comment
Share on other sites

while ( *( (unsigned char *)(0xD40B))) might work too.

But I'm confused - if you are drawing graphics in a normal way, outside of the vertical blanks, then you are by definition not going to be drawing them while the vb is happening, so there's no point in waiting. Any cycles your program gets are always 'after the Vblank', right? Unless the goal is to draw a complete screen before the next vblank happens...?

Anyway, I don't really understand what he's trying to do, so I'm probably off base.

 

Sanny - I didn't really give him a solution, just showed some examples of how to use a little bit of inline assembler to handle interrupts. He's asking for something else i think.

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

The WAITVB approach isn't advisable as generally the VBI will be in progress when the transition back to VCOUNT=0 occurs.

Additionally, with VCOUNT spanning 2 lines, combined with a possible short bit of code in-between, could cause the a fake response.

One way to avoid that is to wait for the line before and then wait for that value to change. e.g.

while ( *( (unsigned char *)(0xD40B)) != 113);
while ( *( (unsigned char *)(0xD40B)) == 113);

But generally there is a mechanism to hook a routine into the VBI interrupt chaining, from a quick search something like the section Smoothing Out Your Scrolling Action provides a reasonable explanation.

Link to comment
Share on other sites

 

#define RTCLOCK (volatile char*)0x12

void wait_for_vblank(void)
{
    char frame = *(RTCLOCK + 2);
    while (*(RTCLOCK + 2) == frame);
}

So it turns out (even mentioned in mapping the Atari book) that location 20 which is one of 3 atari clock timers is set every VBLNAK. So your code makes a lot of sense and it is a direction I have also chosen.

Tnx !

Link to comment
Share on other sites

The WAITVB approach isn't advisable as generally the VBI will be in progress when the transition back to VCOUNT=0 occurs.

Additionally, with VCOUNT spanning 2 lines, combined with a possible short bit of code in-between, could cause the a fake response.

One way to avoid that is to wait for the line before and then wait for that value to change. e.g.

while ( *( (unsigned char *)(0xD40B)) != 113);
while ( *( (unsigned char *)(0xD40B)) == 113);
But generally there is a mechanism to hook a routine into the VBI interrupt chaining, from a quick search something like the section Smoothing Out Your Scrolling Action provides a reasonable explanation.

Thanks wrathchild

Link to comment
Share on other sites

while ( *( (unsigned char *)(0xD40B))) might work too.

But I'm confused - if you are drawing graphics in a normal way, outside of the vertical blanks, then you are by definition not going to be drawing them while the vb is happening, so there's no point in waiting. Any cycles your program gets are always 'after the Vblank', right? Unless the goal is to draw a complete screen before the next vblank happens...?

Anyway, I don't really understand what he's trying to do, so I'm probably off base.

 

Sanny - I didn't really give him a solution, just showed some examples of how to use a little bit of inline assembler to handle interrupts. He's asking for something else i think.

I just wanted to ensure I am drawing at the right time without causing interrupts to overlap.

Thanks !

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