Commodore C64 如何检测 PAL 或 NTSC

Commodore C64 how to detect PAL or NTSC

背景资料

我目前正在使用 KickC [测试版] 为 Commodore C64 开发程序 API,以允许我更轻松地开发小程序、应用程序和可能的一些游戏;我突然想到我可能需要一种方法来检查我的代码在 PAL 或 NTSC 机器上是否是 运行,在后一种情况下,是哪台 NTSC 机器,因为旧的 NTSC C64 比新的 C64 少了一条扫描线版本。

在寻求帮助后,Robin Harbron 向我发送了一个代码片段,该代码片段可以与附加的 CMD SuperCPU(我最终的目标机器)一起使用。由于他把它作为汇编发送,我只好按原样使用大部分,而是使用KickC中的ASM指令,如下:

/**
 * 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
    }
}

在我的脚本顶部,我有这样的全局声明的 getMachineType

unsigned byte getMachineType;

目前,我的 peek() 函数是这样工作的:

/**
 * 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;
}

所以现在我可以确定主机上可用的扫描线数 运行 我的程序,因此更容易创建 PAL 和 NTSC 兼容的可执行文件。

KickC 可从 CSDb.dk, and will build and assemble with Kick Assembler

下载

您分享的汇编代码使用的是VIC-II芯片的SCROLY寄存器($D011)和RASTER寄存器($D012)。

$D011的高位为当前光栅扫描行的最高位,而$D012为低8位。

NTSC 系统有 262(或 261?)扫描线,PAL 有 312。

汇编程序等待设置高位的瞬间,即扫描行 256。

如果设置了高位,它将循环直到未设置:

    __br1:
        lda $d011
        bmi __br1         // read $d011 again if high bit is set

然后在高位清零的情况下循环,一旦置位就退出循环:

    __br2:
        lda $d011
        bpl __br2         // read $ d011 again if high bit is clear

然后失败,从$d012

加载光栅扫描线的低8位

这会在测试高位时将扫描线值的低 8 位存储在 $c000 中,直到再次清除。如果已经清零,则不存储低8位,而是通过__ex1

退出循环
    __br3:
        lda $d012
        bit $d011
        bpl __ex1
        sta $c000
        bmi __br3
    __ex1:
        cli

结果应该是观察到的最高扫描线的数量 - 256。对于具有 262 条扫描线的 NTSC,这是您获得 return 值 6(或其他 NTSC 型号为 5)的地方,它为零-based 所以 5 将是 262 扫描线 NTSC 版本的值。 从零开始的扫描线 312 将是 311,减去 256 = 55,十六进制 $37 ..那里的评论真的应该指定 37 是十六进制的。

您可以在优秀的“Mapping the Commodore 64”一书中找到有关这些寄存器的信息。

您可以将所有这些汇编程序翻译成 C(设置中断禁用位并清除它除外:seicli),只需将值 0xD011 分配给 char * 指针, 和另一个 0xD012 并用 C 代码做同样的事情。

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
}