Reputation: 209
Background Information
I am currently developing a programming API for the Commodore C64 using KickC [in Beta] to allow me to more easily develop small programs, applications and possibly some games; it occurred to me that I may need a way to check if my code is running on PAL or NTSC machines, and in the latter case, which NTSC machine, as the old NTSC C64 has one fewer scanline than the newer C64 version.
After asking about for some help, Robin Harbron sent me a code snippet which works including with a CMD SuperCPU attached (my eventual target machine). As he sent it as assembly, I had to use most of it as it was, but use the ASM directive in KickC, as follows:
/**
* This is the initialisation will
* determine the machine type
* by setting the getMachineType global
* as follows:
* 37 is PAL
* 5 is NTSC (old)
* 6 is NTSC (new)
* 0 (or any other value) is unknown
*
* For safety, the initial value of 0xc000
* is stored into the accumulator and
* pushed onto the stack; it is then
* restored after the getMachineType
* global is set
*
* @author Robin Harbron
*/
void setMachineType() {
asm {
lda $c000
pha
sei
__br1:
lda $d011
bmi __br1
__br2:
lda $d011
bpl __br2
__br3:
lda $d012
bit $d011
bpl __ex1
sta $c000
bmi __br3
__ex1:
cli
}
getMachineType = peek(0xc000);
asm {
pla
sta $c000
}
}
At the top of my script, I have the getMachineType
declared globally like this:
unsigned byte getMachineType;
and at the moment, my peek()
function works like this:
/**
* Returns a single byte from a specific
* memory location
*/
unsigned char peek(unsigned short location) {
unsigned char* memory = (char*)location;
unsigned char returnByte = *memory;
return returnByte;
}
So now I can determine the available number of scan lines on the host machine running my program, and therefore more easily create PAL and NTSC compatible executables.
KickC is available to download from CSDb.dk, and will build and assemble with Kick Assembler
Upvotes: 1
Views: 513
Reputation: 4371
The assembly code you shared is using the SCROLY register ($D011) and the RASTER register ($D012) of the VIC-II chip.
The high bit of $D011 is the most significant bit of the current raster scan line, while $D012 contains the lower 8 bits.
NTSC systems have 262 (or 261?) scan lines, PAL have 312.
The assembler routine waits for the instant the high bit is set , i.e. scan line 256.
IF the high bit is set it loops until it is not set:
__br1:
lda $d011
bmi __br1 // read $d011 again if high bit is set
Then it loops while the high bit is clear, exiting the loop as soon as it becomes set:
__br2:
lda $d011
bpl __br2 // read $ d011 again if high bit is clear
Then it falls through to load the lower 8 bits of the RASTER scanline from $d012
This keeps storing the lower 8 bits of the scanline value in $c000 while testing the high bit until it clears again. If it has cleared, don't store the lower 8 bits, instead exit the loop through __ex1
__br3:
lda $d012
bit $d011
bpl __ex1
sta $c000
bmi __br3
__ex1:
cli
The result should be The number of the highest scanline observed - 256. For NTSC with 262 scanlines this is where you get the return value of 6 (or 5 for that other NTSC model), it's zero-based so 5 would be the value for the 262 scanline NTSC version. Zero based scanline 312 would be 311, minus 256 = 55, in hex $37 .. The comments there should really have specified that the 37 was hexadecimal.
You can find information about these registers in the excellent "Mapping the Commodore 64" book.
You could translate all of this assembler into C (with the exception of setting the interrupt disable bit and clearing it: sei
, cli
), just assign a char * pointer the value 0xD011, and another 0xD012 and do the same thing with C code.
char machineType = 0;
signed char * SCROLY = 0xd011;
char * RASTER = 0xd012;
asm {
sei
}
while (*SCROLY < 0) ; // spin until scaline wraps
while (*SCROLY > 0) ; // spin until high bit set again
while (true) {
char lines = *RASTER;
if (*SCROLY > 0)
break;
machineType = lines;
}
asm {
cli
}
Upvotes: 2