; Made by Alessandro Lo-Presti (Agilo) on May 11, 2008. ; I made this to find out how to generate random numbers and work with a timer. .orgfill 0x1460 ; Start of General Purpose RAM. .orgfill 0x2100 ; Start of ROM space. .db "MN" ; ROM ID (do not change or validation will fail). .include "irq.asm" ; Game cart IRQ vectors. ; Pokemon Mini game header. .orgfill 0x219E ; 0x219E - 0x21A3 must be empty. .orgfill 0x21A4 ; Start of "NINTENDO" string. .db "NINTENDO" ; Magic signature checked by the BIOS. .db "RNDM" ; Game code, 4 bytes. .db "Randomness" ; Name of ROM, 12 chars max. .orgfill 0x21BC ; Will contain the following string: .db "2P" ; At this time it's unknown what "2P" stands for. .orgfill 0x21D0 ; End of rom header, start of program code. .include "irqhandlers.asm" ; Game cart IRQ routine handlers. ; Code entry point. start: movw sp, 0x2000 ; Set SP to end of general purpose RAM (stack grows down). movw nn, 0x2000 ; NN register points to hardware registers. ; Enable primary interrupts on all keys. movb [nn+0x21], 0x0C ; Enable secondary interrupt for the power button (CPU will jump to 0x215C). movb [nn+0x25], (1<<7) ; Clear all flags. movb flags, 0 ; Enable LCD: 12x16, Global rendering, sprite/BG rendering, Non-inverted. movb [nn+0x80], 0b00001110 ; Set refresh rate (60Hz / 2 = 30Hz). movb [nn+0x81], 0b00001001 ; Store the address of the sprite in the memory address register. movw x2, sprtest ; Write the address to a register of the LCD so the PM knows where to look. movw [0x2087], x2 ; Temporarily use N as a pointer to the OAM. movw nn, 0x1300 ; Set up sprite settings: X coordinate. movb [nn+0], 20 ; Set up sprite settings: Y coordinate. movb [nn+1], 20 ; Set up sprite settings: ID. movb [nn+2], 0 ; Set up sprite settings: OAM attributes. movb [nn+3], (1<<3) ; Restore N. movw nn, 0x2000 ; Enable timer 1. movb [nn+0x40], 1 ; b = 95 (there are 95 blocks of 8x8 pixels on the screen). movb b, 95 ; Set HL to the end of the Tile Map data. movw HL, 0x1360+95 bglinear: ; Fill the background with tileblock 95 (which is blank). movb [HL], 95 ; Decrement HL. dec HL ; Repeat while b is not zero. jdbnz bglinear ; Store the address of the image in the memory address register. movw x1, scrtest ; Write the address to a register of the LCD so the PM knows where to look. movw [0x2082], x1 ; Variable which defines A button as being held down. movb a, 0 ; Logics start here. PM_loop: ; Check if A button was pressed. test [nn+0x52], 0x01 jnz _skip_setpos ; If A button is being held, skip this. test a, 1 jnz _end_setpos ; Save a ("held" variable) on the stack. push a ; Get the lower byte from the timer. movb b, [0x2041] ; Random number algorithm from: http://www.dontronics.com/psbpix/random.html ; Changed slightly to work on a byte instead of a word. ; ; Basically comes down to (pseudocode): ; random = timervalue ; bitmask = (random[7]^random[6])^(random[4)^random[2]) ; random <<= 1 ; random |= bitmask ; Copy timer number to a. movb a, b ; Save a copy on the stack push b ; AND the timer number with 10000000 and shift it to the LSB. and b, 0b10000000 shr b shr b shr b shr b shr b shr b shr b ; Number is now at the LSB. ; Do the same for the value in a (only AND it with 01000000). and a, 0b01000000 shr a shr a shr a shr a shr a shr a ; Number is now at the LSB. ; XOR the two values and save the result into a (b is now irrelevant). xor a, b ; Get the saved timer value into h. pop h ; Save it back onto the stack (value will remain in the register). push h ; AND the timer number with 00010000. and h, 0b00010000 ; The PM won't allow shifts to occur in h, so move it (temporarily) to b. movb b, h ; Shift it so the number that was AND'd is set to the LSB. shr b shr b shr b shr b ; Move it back to h where it belongs. movb h, b ; Get the saved timer value into l. pop l ; Save it back onto the stack (value will remain in the register). push l ; AND the timer number with 00000100. and l, 0b00000100 ; The PM won't allow shifts to occur in l, so move it (temporarily) to b. movb b, l ; Shift it so the number that was AND'd is set to the LSB. shr b shr b ; Move it back to h where it belongs. movb l, b ; Now it gets tricky, as the PM won't allow us to XOR h and l directly. ; Save the old XOR bitmask onto the stack (on top of the timer value). push a ; Move h into b and l into a. movb b, h movb a, l ; XOR the values (result gets stored into a). xor a, b ; Get the old XOR bitmask from stack and place it in b. pop b ; XOR the two masks that reside in b and a (result is saved in a). xor a, b ; Get the original timer value and place it in b. pop b ; a is now the XOR bitmask. ; b is now the original timer value. ; Shift b left once (make room for the bitmask). shl b ; Add the bitmask to the original timer value and save the result into a. or a, b ; Old method (works fine but I didn't like its randomness): ; rorc b ; rorc b ; movb a, [nn+0x41] ; rolc a ; xor a, b ; Set the X coordinate of the sprite to our random number. movb [0x1300], a ; Shuffle the bits around for the Y coordinate of the sprite. rorc a rorc a ; Shift right twice to ensure it'll be displayed within the screen. shr a shr a ; Set the Y coordinate of the sprite to our random number. movb [0x1301], a ; restore a ("held" variable) from the stack. pop a ; Save button status as being held down. movb a, 1 jmp _end_setpos _skip_setpos: ; Restore button status as not being held down anymore. movb a, 0 _end_setpos: ; Wait for vblank. call vblank jmp PM_loop ; Wait a vblank. vblank: mov [nn+0x27], 0x80 _sloop: test [nn+0x27], 0x80 jz _sloop ret ; Sprites are 16x16 pixels (64 bytes large). .align 64 ; Include the sprite. sprtest: .incbin evildude.bin ; Blocks are 8x8 pixels (8 bytes large) .align 8 ; Include the image. scrtest: .incbin logo-agilo-96x64_bw.bin