+ SECTION "a",HOME
+
+main:
+ ld c, $51 ; Note that we are alive.
+ ld a, $FF
+ ld [c],a
+
+ ld sp, $DFF0
+
+ ld hl, signon
+ call puts
+
+ call memtest
+
+ call insntest
+
+ call waitsw
+
+ jr main
+
+signon:
+ db $0D,$0A,$1B,"[1mFPGABoy Diagnostic ROM",$1B,"[0m",$0D,$0A,0
+
+; Memory tester: writes h ^ l to all addresses from C000 to DF80.
+memtest:
+ ld hl,memteststr
+ call puts
+
+ ld hl, $C001 ; Write loop
+.wr:
+ ld a,h
+ xor l
+ ld [hli],a
+ ld a, $DF
+ cp h
+ jr nz, .wr
+ ld a, $80
+ cp l
+ jr nz, .wr
+
+ ld hl, $C001 ; Read loop
+.rd:
+ ld a,h
+ xor l
+ ld b,a
+ ld a, [hli]
+ cp b
+ jr nz, .memfail
+
+ ld a, $DF
+ cp h
+ jr nz, .rd
+ ld a, $80
+ cp l
+ jr nz, .rd
+
+ ld hl, testokstr ; Say we're OK
+ call puts
+ ret
+.memfail: ; Say we failed (sadface)
+ ; decrement hl the easy way
+ ld a,[hld]
+ 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
+ ld b,$0F
+ and b
+ ld b,$30
+ add b
+ call putc
+ pop af
+ ld b,$0F
+ and b
+ ld b,$30
+ add b
+ call putc
+ ret
+
+; Wait for switches to be flipped on and off again.
+waitsw:
+ ld hl,waitswstr
+ call puts
+
+ ld c, $51
+ xor a
+ ld [c],a
+
+ ld b, $0
+.loop1:
+ ld a,[c]
+ cp b
+ jr z,.loop1
+.loop2:
+ ld a,[c]
+ cp b
+ 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
+ ld b, $00
+ cp b
+ 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
+ ld b, $20
+ cp b
+ jr nc,.fail
+ ld a, $20
+ ld b, $10
+ cp b
+ jr c,.fail
+
+ ; Test CPL
+ ld hl, .cplfail
+ ld a, $55
+ ld b, $AA
+ cpl
+ cp b
+ jr nz,.fail
+
+ ld hl, .ok
+ call puts
+ ret
+.fail:
+ 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
+.testfailed:
+ db " test failed.",$0D,$0A,0
+.ok:
+ db "OK!",$0D,$0A,0
+
+; Serial port manipulation functions.
+putc:
+ ld b, 0
+ ld c, $50
+ push af
+.waitport:
+ ld a,[c]
+ cp b
+ jr nz,.waitport
+ pop af
+ ld [c],a
+ ret
+
+puts:
+ ld a, [hli]
+ ld b, $00
+ cp b
+ ret z
+ call putc
+ jr puts