section "end",HOME[1024] nop SECTION "a",HOME[$00] start: jp main section "vbl",HOME[$40] jp vbl section "lcdc",HOME[$48] jp lcdc section "tmro",HOME[$50] jp tmro main: ld a, $FF ld c, $51 ld [c], a ld sp, $DFF0 ld a, $04 ;start timer, 4.096KHz ld c, $07 ld [c], a ld hl, $DF81 xor a ld [hli], a ld [hli], a ld hl, signon call puts ld a, $91 ld [$FF40], a call putscreen ei call memtest call insntest call waitsw di jr main signon: db $0D,$0A,$1B,"[1mFPGABoy Diagnostic ROM",$1B,"[0m",$0D,$0A,0 tiles: db %01111100 db %11000110 db %11000110 db %11111110 db %11000110 db %11000110 db %11000110 db %00000000 db %11111100 db %11000110 db %11000110 db %11111100 db %11000110 db %11000110 db %11111100 db %00000000 db %01111100 db %11000110 db %11000010 db %11000000 db %11000010 db %11000110 db %01111100 db %00000000 db %11111100 db %11000110 db %11000110 db %11000110 db %11000110 db %11000110 db %11111100 db %00000000 putscreen: LD A,$fc ; $001d Setup BG palette LD [$FF47],A ; $001f ; Wait for vblank call .vblwait ld hl, $8000 ; Copy two tiles. ld de, tiles ld c, $20 .cloop: ld a, [de] inc de ld [hli], a ld [hli], a dec c xor a cp c jr nz, .cloop ld hl, $9800 .vloop: call .vblwait ld c, $40 ld b, 0 .loop: ld a, b inc b and $03 ld [hli], a ld a, h cp $9C ret z dec c xor a cp c jr nz,.loop jr .vloop .vblwait: .stat1: ld a, [$FF41] ; STAT and $03 cp $00 jp nz, .stat1 .stat2: ld a, [$FF41] and $03 cp $01 jr nz, .stat2 ret vbl: PUSH AF PUSH BC PUSH DE PUSH HL xor a ld [$FF0F], a ld a, [$FF51] bit 7, a jr z, .nothing bit 0, a call nz, .scyup bit 1, a call nz, .scydown bit 2, a call nz, .scxup bit 3, a call nz, .scxdown .nothing: POP HL POP DE POP BC POP AF RETI .scyup: ld hl, $FF42 inc [hl] ret .scydown: ld hl, $FF42 dec [hl] ret .scxup: ld hl, $FF43 inc [hl] ret .scxdown: ld hl, $FF43 dec [hl] ret lcdc: PUSH AF PUSH BC xor a ld [$FF0F], a POP BC POP AF reti tmro: PUSH AF PUSH BC PUSH DE PUSH HL xor a ld [$FF0F], a ld c, $45 ; LYC ld a, [c] inc a ld [c], a ld a, [$DF82] cp 0 jr z, .noprint ld a, $41 ; print A call putc .noprint: ld a, [$DF81] inc a ld [$DF81], a ld [$FF51], a POP HL POP DE POP BC POP AF RETI ; Memory tester: writes h ^ l to all addresses from C000 to DF80. memtest: ld hl,memteststr call puts ld hl, $C000 ; Write loop .wr: ld a,h xor l ld [hli],a ld a, h cp $DF jr nz, .wr ld a, l cp $80 jr nz, .wr ld hl, $C000 ; Read loop .rd: ld a,h xor l ld b,a ld a, [hli] cp b jr nz, .memfail ld a, h cp $DF jr nz, .rd ld a, l cp $80 jr nz, .rd ld hl, testokstr ; Say we're OK call puts ret .memfail: ; Say we failed (sadface) ; decrement hl the easy way dec [hl] push hl ld hl, failatstr call puts pop hl ld a, h call puthex ld a, l call puthex ld a, $0A call putc ld a, $0D call putc ret memteststr: db "Testing memory from $C000 to $DF80...",0 testokstr: db " OK!",$0D,$0A,0 failatstr: db " Test failed at $",0 puthex: ; Put two hex nibbles to the serial console. push af rra rra rra rra and $0F add $30 call putc pop af and $0F add $30 call putc ret ; Wait for switches to be flipped on and off again. waitsw: ld hl,waitswstr call puts ld hl,$DF82 ld a, 1 ld [hl], a ld c, $51 xor a ld [c],a .loop1: ld a,[c] cp $0 jr z,.loop1 .loop2: ld a,[c] cp $0 jr nz,.loop2 ret waitswstr: db "Diagnostic ROM complete; flip switches to nonzero and then to zero to reset. Expect A.",$0D,$0A,0 ; Core instruction basic acceptance tests. insntest: ld hl, .insnteststr call puts ; Test PUSH and POP. ld b, $12 ld c, $34 ld d, $56 ld e, $78 push bc pop de ld hl, .pushpopfail ld a, d cp b jr nz,.fail ld a, e cp c jr nz,.fail ; Test ALU (HL). ld hl, .ff ld a, $FF xor [hl] ld hl, .xorhlfail jr nz, .fail ; Test JP (HL) ld hl, .jphl jp [hl] ld hl, .jphlfail jr .fail rst $00 .jphl: ; Test JR ld a, $FF cp $0 jr nz,.jr ld hl, .jrfail jr .fail rst $00 .jr: ; Test inc16 ld d, $12 ld e, $FF ld hl, .inc16fail inc de ld a, $13 cp d jr nz, .fail ld a, $00 cp e jr nz, .fail ; Test CP. ld hl, .cpfail ld a, $10 cp $20 jr nc,.fail ld a, $20 cp $10 jr c,.fail ; Test CPL ld hl, .cplfail ld a, $55 cpl cp $AA jr nz,.fail ; Test DI/EI delay di ld hl, .dinocausefail ld c, $0F ; First, wait until an interrupt happens... .wait: ld a, [c] and $04 cp 0 jr z, .wait ei ; Now make sure that an IRQ didn't happen on EI/DI di ld a, [c] and $04 cp 0 jr z, .fail ld hl, .dicausefail ei ; Make sure that an IRQ does happen on EI/NOP/DI nop nop di ld a, [c] and $04 cp 0 jr nz, .fail ei ld hl, .ok call puts ret .fail: ei call puts ld hl, .testfailed call puts ret .insnteststr: db "Testing instructions... ",0 .pushpopfail: db "PUSH/POP",0 .ff: db $FF .xorhlfail: db "XOR [HL]",0 .jphlfail: db "JP [HL]",0 .jrfail: db "JR",0 .cpfail: db "CP",0 .cplfail: db "CPL",0 .inc16fail: db "INC16",0 .dinocausefail: db "DI/EI does not cause interrupt",0 .dicausefail: db "DI/NOP/EI cause interrupt",0 .testfailed: db " test failed.",$0D,$0A,0 .ok: db "OK!",$0D,$0A,0 ; Serial port manipulation functions. putc: ld c, $50 push af .waitport: ld a,[c] cp $00 jr nz,.waitport pop af ld [c],a ret puts: ld a, [hli] cp $00 ret z call putc jr puts