Having a bit of a struggle with getting PMG to work correctly with C. I've been trying to port the Action! code from PMG.ACT (from the Toolkit). I'm hitting a wall setting the PM Base address. Its the MSB of the actual address and is stored in PMBASE (54279) as a single byte. No matter what I do, if I peek the address after setting - it is ALWAYS 255, which is wrong. In my test case it should be 148. This causes the Players to displays "snow" when activated (see black areas on this screen shot). Don't mind all the debug messaging. Those values are all correct, EXCEPT PMB. It should match wPMB shift (which is wPMB / 256):
The player width is being set correctly, as it the x location. Presumably the y location is as well since the player is bitmapped into the correct memory spot - I validated this with a string of peeks after setting, just not in this output.
I beleive the correct order to setup PMG is:
DMACTL or SDMCTL (shadow)
GRACTL
PMBASE
GPRIOR
I have tried moving PMBASE before GRACTL and after GPRIOR, but nothing seems to work. PMBASE is ALWAYS ALWAYS ALWAYS 255 and I cant figure out why.
Heres a portion of the code dealing with setting it:
// If requested mode is 0, turn off PMG
if (bN == 0) {
DMACTL = 34;
GRACTL = 0;
// Save mode
bPMM = bN;
} else {
// If requested mode is 1 (single line)
if (bN == 1) {
DMACTL = 62;
//SDMCTL = 62;
} else {
// Else double line
DMACTL = 46;
//SDMCTL = 46;
}
// Save memtop
wOHM = MEMTOP;
gotoxy(0,16);
printf("Old MEMTOP(wOHM): %u\n", wOHM); // 39967
// Find PM base address (bitwise and)
wPMB = (MEMTOP - aMS[bN] - 128) & aAM[bN]; // 38815 & 64512 = 37888
printf("wPMB=%u\n", wPMB); // 37888
// PMBASE = (wPMB >> 8); // >> 8;
// PMBASE = wPMB / 256;
PMBASE = (byte) (wPMB / 256);
// POKE(54279,148);
printf("wPMB shift=%u, PMB=%u\n", wPMB >> 8, PEEK(54279)); // 148
printf("APPMHI=%u\n", APPMHI); // 23583
// If address is less than application himem, dont enable
if (wPMB < APPMHI) {
DMACTL = 34;
GRACTL = 0;
} else {
// Set new memtop
MEMTOP = wPMB + aPMW[bN];
printf("New MEMTOP: %u\n", MEMTOP);
GRACTL = 3;
GPRIOR = (GPRIOR & 0xC0) | 1;
printf("GPRIOR=%d\n", GPRIOR);
// Save mode
bPMM = bN;
}
}
PMBASE is a define which acts like a bi-directional pointer:
#define PMBASE *((word *) 0xD407)
So you can do things like:
x=PMBASE
PMBASE=y
I've also tried chaning this to a byte since it holds a single byte. Nada. I also put the same debug in the Action! code and ran it. They numbers check out, except APPMHI (presumably because a DOS and a cart is loaded with Action!).
The rest of the code seems to work. Any ideas why I can't set PMBase?
Following the Compute books instructions I redid the routine that sets up PM. Still not getting valid results, but I think its closer.
The players are cleared after the graphics are setup which is done in another routine.
I am a bit defeated at this point and going to step away and come back with a fresh perspective at some point in time, unless I get some new understanding of where I'm failing. At this point I think its something CC65 is doing with the memory segments -or- the display list is getting knocked out. One thing the book says is to give a graphics 0 command after setup so the OS will move the display and display list below the new RAMTOP. I cant figure out how to do that in CC65.
I dont have a screen shot, essentially it gets through this procedure, then starts clearing the players. Once it hits the 2nd pleyer, the screen goes into graphics 13 and you can see some text flash in and out of the first line.
void PMGraphics(byte bN) { // Address Masks, and Mem Sizes for each mode unsigned int aAM[4] = { 0, 0xF800, 0xFC00 }, aMS[4] = { 0, 0x800, 0x400 }; // Default # pages to reserve for gr0 display byte bR = 4; // RAMTOP worker byte bT = 0; // Move all players and missiles off screen memset((byte *) &(HPOSP0), 0, 8); // word // Ensure tracking coords are all 0 memset(aPMH, 0, 8); memset(aPMV, 0, 8); // Set sizes to 0 and clear shapes of all 5 players memset((byte *) &(SIZEP0), 0, 10); // word // If current PM mode is not 0, default everything // as we restart if (bPMM != 0) { SDMCTL = 34; // Default GRACTL = 0; } // If requested mode is 1 (single line) if (bN == 1) { // Add 8 pages for single line res bR += 8; } else { // Add 4 pages for double line res bR += 4; } // If requested mode is 0, turn off PMG if (bN == 0) { SDMCTL = 34; GRACTL = 0; // Reset RAMTOP ? **************************************************** // Save mode bPMM = bN; } else { // Get current RAMTOP bT = RAMTOP; // Lower RAMTOP by # reserve pages RAMTOP = RAMTOP - bR; gotoxy(0,16); printf("Old RAMTOP=%u (%u), R=%d\n", bT, bT * 256, bR); // 40960 (160) printf("New RAMTOP=%u (%u)\n", RAMTOP, RAMTOP * 256); printf(" APPMHI=%u\n", APPMHI); // 23583 // Set PMBASE to new RAMTOP PMBASE = RAMTOP * 256; wPMB = RAMTOP * 256; printf("wPMB=%u (%u)\n", wPMB >> 8, wPMB); // 37888 printf(" PMBASE=%u\n", PEEK(54279U)); // 148 (* 256 = 37888) // If requested mode is 1 (single line) if (bN == 1) { SDMCTL = 62; } else { // Else double line SDMCTL = 46; } // Set player priority GPRIOR = (GPRIOR & 0xC0) | 1; printf(" GPRIOR=%d\n", GPRIOR); // Turn on PMG GRACTL = 3; // Save mode bPMM = bN; } }
I'm still not sure whats going on. I suspect it may be where in memory the PMG is being reserved, although it is ending on a $xx00 boundary. I read Computes 1st and 2nd book of Atari graphics related to PMG, and going to take a new approach rather than trying to port from Action!.
Don't you need to clean out the PM map and redraw in each vertical or horizontal movement?