assemblygnugnu-assemblergameboy

How can I generate a checksum for this cartridge header?


I'm trying to generate a cartridge header checksum for a Game Boy cart. The cartridge header checksum is defined as this C-like pseudocode:

unsigned char checksum = 0;
for(unsigned char *p = 0x0134; p < 0x014D; p++)
    x += ~*p;

or alternatively:

unsigned char checksum = 0;
for(unsigned char *p = 0x0134; p < 0x014D; p++)
    x = x - *p - 1;

This data starts at the address 0x0104. The address 0x0134 corresponds to the beginning of title. The address 0x014D corresponds to the beginning of header_checksum.

Note that for demonstration purposes I'm using hexadecimal escape sequences in .ascii strings. This won't actually work because \x isn't recognized by GNU as.

nintendo_logo:
    # Nintendo logo. Must be present and unmodified.
    .ascii "\xce\xed\x66\x66\xcc\x0d\x00\x0b\x03\x73\x00\x83\x00\x0c\x00\x0d"
    .ascii "\x00\x08\x11\x1f\x88\x89\x00\x0e\xdc\xcc\x6e\xe6\xdd\xdd\xd9\x99"
    .ascii "\xbb\xbb\x67\x63\x6e\x0e\xec\xcc\xdd\xdc\x99\x9f\xbb\xb9\x33\x3e"
title:
    # Title. At most 11 characters and zero-padded if smaller.
    # GameBoys use 16 characters.
    TITLE_SIZE = 11
    .ascii "ABCDEF"
    .fill TITLE_SIZE-(.-title)
manufacturer:
    .ascii "ABCD"
cgb_f:
    # Color Game Boy flag. 0x80 means that the game supports
    # Color Game Boy functions, but still works on the original Game Boy.
    .byte 0x80
new_licensee:
    # Company or publisher ASCII code. 00 is none.
    .ascii "00"
sgb_f:
    # Super Game Boy flag. 3 means it has support for the SGB, 0 means no.
    # I might implement color for the SGB
    .byte 0x03
cart_type:
    # Memory bank controller used and any additional hardware.
    # 0x00 is rom-only.
    .byte 0x00
rom_size:
    # ROM size in terms of 32KB << B
    .byte 0x00
ram_size:
    # The amount of externam RAM in the catridge
    .byte 0x00
is_japan:
    # If the byte is 0x00 it's for Japan, if 0x01 anywhere else
    .byte 0x01
old_licensee:
    # hex value of company/publisher
    # Super Game Boy needs 0x33 to work
    .byte 0x33
version:
    # version of the game
    .byte 0x00
header_checksum:
    # TODO: How to calculate this?
    sum = add(title, version)
    .byte sum

How can I calculate this checksum? If possible, is there a way to do it using assembler directives?


Solution

  • GAS directives can't read bytes that have already been emitted into the output file.

    If you wanted assemble-time calculation of something, I think you could do it with a GAS .macro that used its arg in both an assemble-time expression updating an assembler variable, and as an operand for .byte.

    But you'd need to do it in expressions that work as numbers, which would probably rule out convenient .ascii string literals.

    In this case it's probably more trouble than it's worth to convert over to using a macro like
    byte_cksum_accum 0xce,0xed,0x66,0x66,0xcc,0x0d,0x00, ....

    Such a macro could maybe use .set cksum, cksum + ~\1 or something like that, as well as .byte \1. Looping over multiple GAS macro args is done by writing a recursive .macro. So I think it's possible. GAS also has a .altmacro syntax that allows i = i + 1. I haven't used GAS macros much myself.

    Better alternative: use a C program + build rules + .incbin

    Even better alternative?

    Or write a C program that modifies a binary