Yaron Nir Posted September 29, 2018 Share Posted September 29, 2018 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 Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 29, 2018 Author Share Posted September 29, 2018 if the above wasn't clear, no ASM examples please. only native CC65 code Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted September 29, 2018 Share Posted September 29, 2018 example here... CA65 gives you an object file you can link with an call from your C code so best to stick to asm Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 29, 2018 Author Share Posted September 29, 2018 Wrathchild, you are becoming my CC65 guru Isn't there native solution? only link to asm code? Quote Link to comment Share on other sites More sharing options...
baktra Posted September 29, 2018 Share Posted September 29, 2018 Wrathchild, you are becoming my CC65 guru Isn't there native solution? only link to asm code? No, assembler is really required. Anyway, DLI routines are typically short (out of necessity), so a bit of assembler is not a big deal. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted September 29, 2018 Share Posted September 29, 2018 (edited) 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 September 29, 2018 by danwinslow 1 Quote Link to comment Share on other sites More sharing options...
ivop Posted September 29, 2018 Share Posted September 29, 2018 ... 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) Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 29, 2018 Author Share Posted September 29, 2018 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? Quote Link to comment Share on other sites More sharing options...
danwinslow Posted September 29, 2018 Share Posted September 29, 2018 (edited) 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 September 29, 2018 by danwinslow Quote Link to comment Share on other sites More sharing options...
danwinslow Posted September 29, 2018 Share Posted September 29, 2018 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? Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 29, 2018 Author Share Posted September 29, 2018 Yaron - Why are you trying to wait until vblank is finished? well, that is the safest way to draw things on screen without messing things up right? Quote Link to comment Share on other sites More sharing options...
ivop Posted September 29, 2018 Share Posted September 29, 2018 (edited) 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 September 29, 2018 by ivop Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 29, 2018 Author Share Posted September 29, 2018 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..... Quote Link to comment Share on other sites More sharing options...
sanny Posted September 29, 2018 Share Posted September 29, 2018 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? Quote Link to comment Share on other sites More sharing options...
antrykot Posted September 29, 2018 Share Posted September 29, 2018 #define RTCLOCK (volatile char*)0x12 void wait_for_vblank(void) { char frame = *(RTCLOCK + 2); while (*(RTCLOCK + 2) == frame); } 1 Quote Link to comment Share on other sites More sharing options...
danwinslow Posted September 29, 2018 Share Posted September 29, 2018 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. Quote Link to comment Share on other sites More sharing options...
ivop Posted September 29, 2018 Share Posted September 29, 2018 what could you suggest as aleternative to the code above. keep it C please..... Assuming PEEK is a proper macro... #define WAITVB while(PEEK(0xd40b)) ..... WAITVB; ...... Quote Link to comment Share on other sites More sharing options...
danwinslow Posted September 29, 2018 Share Posted September 29, 2018 (edited) 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 September 29, 2018 by danwinslow 1 Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted September 29, 2018 Share Posted September 29, 2018 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. Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 30, 2018 Author Share Posted September 30, 2018 #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 ! Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 30, 2018 Author Share Posted September 30, 2018 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 Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 30, 2018 Author Share Posted September 30, 2018 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 ! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.