; Times all official and unofficial 6502 instructions except the ; 8 branch instructions (Bxx) and the 12 halt instructions (HLT). ; See readme.txt for operation and overview of algorithm used. counter = $10 ; number of iterations of test loop nmi_flag = $12 ; tells NMI what to do test_index = $13 ; opcode being tested test_mode = $14 ; 0 = normal, $FE = page crossing type_mask = $15 ; which subset of instructions to test test_code = $200 ; test loop is copied here test_code_page = $02 .org $202 irq: .code reset: ; Standard NES init sei lda #0 sta $2000 sta $2001 jsr wait_vbl jsr wait_vbl cld jsr init_console lda #$A5 ; tell runtime that console is inited sta $7F1 ; Print title ldx #title_str.msb ldy #title_str.lsb jsr print_str jsr console_newline jsr console_newline ; Poll joypad to determine which tests to run lda #1 sta $4016 lda #0 sta $4016 lda $4016 ; A button status and #$01 beq + ldx #official_nop_str.msb ldy #official_nop_str.lsb lda #$70 jmp set_type : lda $4016 ; B button status and #$01 beq + ldx #all_instrs_str.msb ldy #all_instrs_str.lsb lda #$30 jmp set_type : lda #$F0 ldx #official_str.msb ldy #official_str.lsb set_type: sta type_mask jsr print_str jsr console_newline jsr console_newline ; Disable APU IRQ lda #$C0 sta $4017 ; Start NMI lda #0 sta nmi_flag lda #$80 sta $2000 ; Be sure basic NMI timing is correct lda #$F7 sta counter + 1 lda #$18 sta counter ldx #255 stx nmi_flag : cpx nmi_flag beq - dex : cpx nmi_flag bne check_nmi_time inc counter bne - inc counter + 1 jmp - check_nmi_time: lda counter + 1 bne nmi_time_wrong lda counter cmp #12 bcs nmi_time_wrong ; Begin tests lda #2 sta test_mode ldx #0 jmp test_loop nmi_time_wrong: lda #0 sta $2000 ldx #nmi_time_str.msb ldy #nmi_time_str.lsb jsr print_str error_beep_exit: lda #1 jsr debug_beeps jmp forever .code ; Print current opcode print_opcode: ldx #error_str.msb ldy #error_str.lsb jsr print_str lda test_index jsr print_a lda test_mode cmp #2 beq + ldx #cross_str.msb ldy #cross_str.lsb jsr print_str : jsr console_newline jsr console_newline rts .code ; NMI handler : rti nmi: dec nmi_flag bmi - beq check_time ; Restore overwritten stack values to $02 pla pla pla lda #$02 pha pha pha ; Run test loop until next NMI interrupts it lda test_mode tay tax jmp test_code error: lda #0 sta $2000 jsr print_opcode ldx #unknown_str.msb ldy #unknown_str.lsb jsr print_str jsr console_newline jsr console_newline lda counter + 1 ldy counter jsr print_ay jmp error_beep_exit clock_table: ; doubled entries are to subtract value greater than $FF .db $A5, $A5, $87, $86, $E1, $BE, $A2, $8D, $7C, $6D, $08 clock_times: .db $01, $00, $02, $00, $03, $04, $05, $06, $07, $08, $09 ; Calculate number of clocks instruction took check_time: cld ; avoid buggy NES emulator breaking test lda counter ldx counter + 1 ldy #11 clock_loop: dey bmi error sec sbc clock_table,y bcs no_carry dex no_carry: cpx #0 bmi error bne clock_loop cmp #12 bcs clock_loop lda clock_times,y beq error ; Compare result with correct timing ldx test_index ldy test_mode cpy #2 bne + cmp instr_times,x beq next_instr ldy instr_times,x jmp print_error : cmp instr_times_cross,x beq next_instr ldy instr_times_cross,x ; Print measured and correct times print_error: sta <2 sty <3 lda #0 sta $2000 jsr print_opcode ldx #time_str.msb ldy #time_str.lsb jsr print_str lda <2 ora #$30 jsr print_char jsr console_newline jsr console_newline ldx #correct_str.msb ldy #correct_str.lsb jsr print_str lda <3 ora #$30 jsr print_char jmp error_beep_exit ; Report success done: lda #0 sta $2000 ldx #passed_str.msb ldy #passed_str.lsb jsr print_str lda #2 jsr debug_beeps jmp forever next_instr: ; Next instruction : inx bne test_loop ; Toggle between normal and page crossing lda test_mode eor #1 sta test_mode cmp #2 beq done test_loop: lda instr_types,x and type_mask ; skip instructions not being tested bne - stx test_index ; Copy test_code to RAM lda instr_types,x ; get instr length and #$07 tay lda test_addrs_lsb - 1,y sta <0 lda test_addrs_msb - 1,y sta <1 ldy #0 txa : sta test_code,y iny lda (0),y cpy #16 bne - ; Special case for JSR and JMP cpx #$20 beq jsr_jmp cpx #$4C bne + jsr_jmp: lda #$02 ; change address to $0203 sta test_code + 2 lda #$03 sta test_code + 1 : ; Set zero-page values so the addressing modes use the following ; when X and Y = $02/$03 ; zp $FD ; zp,x $FF/$00 ; abs $03FD ; abs,x/y $03FF/$0400 ; (ind,x) $02FD/$0302 ; (ind),y $02FF/$0300 ; JMP (ind) $0203 lda #$02 sta <$00 lda #$03 sta <$01 lda #$FD sta <$FD lda #$02 sta <$FE lda #$FD sta <$FF lda test_mode ; $A3 LAX (ab,X) will load X from these two sta $02FD sta $0302 lda #$03 ; JMP ($03FD) will use this address sta $03FD lda #$02 sta $03FE ; Fill stack with the value $02 (for RTS/RTI test) ldx #$FF txs inx lda #$02 : pha inx bne - ; Load timer with -$6C4 lda #$F9 sta counter + 1 lda #$3C sta counter ; Tell NMI to run test code lda #2 sta nmi_flag : cmp nmi_flag beq - jmp error .code test_1: : nop inc counter bne - inc counter + 1 jmp test_code test_2: : sta <$FD inc counter bne - inc counter + 1 jmp test_code test_3: : sta $03FD inc counter bne - inc counter + 1 jmp test_code test_addrs_lsb: .db test_1.lsb, test_2.lsb, test_3.lsb test_addrs_msb: .db test_1.msb, test_2.msb, test_3.msb .code ; $4n = unofficial NOPs and $EB equivalent of SBC #imm ; $4n = all other unofficial opcodes ; $0n = official opcodes ; -1 = not tested ; n = instruction length instr_types: ; 0 1 2 3 4 5 6 7 8 9 A B C D E F .db 2, 2, -1,$42,$82, 2, 2,$42, 1, 2, 1,$42,$83, 3, 3,$43 ; 0 .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 1 .db 3, 2, -1,$42, 2, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; 2 .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 3 .db 2, 2, -1,$42,$82, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; 4 .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 5 .db 3, 2, -1,$42,$82, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; 6 .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 7 .db $82, 2,$82,$42, 2, 2, 2,$42, 1,$82, 1,$42, 3, 3, 3,$43 ; 8 .db -1, 2, -1,$42, 2, 2, 2,$42, 1, 3, 1,$43,$43, 3,$43,$43 ; 9 .db 2, 2, 2,$42, 2, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; A .db -1, 2, -1,$42, 2, 2, 2,$42, 1, 3, 1,$43, 3, 3, 3,$43 ; B .db 2, 2,$82,$42, 2, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; C .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; D .db 2, 2,$82,$42, 2, 2, 2,$42, 1, 2, 1,$82, 3, 3, 3,$43 ; E .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; F ; Bxx HLT ; Clocks when no page crossing occurs instr_times: ; 0 1 2 3 4 5 6 7 8 9 A B C D E F .db 7,6,0,8,3,3,5,5,3,2,2,2,4,4,6,6 ; 0 .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 1 .db 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6 ; 2 .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 3 .db 6,6,0,8,3,3,5,5,3,2,2,2,3,4,6,6 ; 4 .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 5 .db 6,6,0,8,3,3,5,5,4,2,2,2,5,4,6,6 ; 6 .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 7 .db 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; 8 .db 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 ; 9 .db 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; A .db 0,5,0,5,4,4,4,4,2,4,2,4,4,4,4,4 ; B .db 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; C .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; D .db 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; E .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; F ; Clocks when page crossing occurs instr_times_cross: ; 0 1 2 3 4 5 6 7 8 9 A B C D E F .db 7,6,0,8,3,3,5,5,3,2,2,2,4,4,6,6 ; 0 .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 1 .db 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6 ; 2 .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 3 .db 6,6,0,8,3,3,5,5,3,2,2,2,3,4,6,6 ; 4 .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 5 .db 6,6,0,8,3,3,5,5,4,2,2,2,5,4,6,6 ; 6 .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 7 .db 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; 8 .db 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 ; 9 .db 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; A .db 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 ; B .db 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; C .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; D .db 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; E .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; F title_str: .db "6502 TIMING TEST (16 SECONDS)",0 official_str: .db "OFFICIAL INSTRUCTIONS ONLY",0 official_nop_str: .db "OFFICIAL + NOP",0 all_instrs_str: .db "OFFICIAL + UNDOCUMENTED",0 passed_str: .db "PASSED",0 error_str: .db "FAIL OP :",0 cross_str: .db "WITH PAGE CROSS",0 time_str: .db "EMULATOR: ",0 correct_str: .db "CORRECT : ",0 unknown_str: .db "UNKNOWN ERROR",0 nmi_time_str: .db "BASIC TIMING WRONG",0 ; Prints string at address XY print_str: stx <1 sty <0 ldy #0 lda (0),y jsr print_char iny lda (0),y : jsr print_char_no_wait iny lda (0),y bne - rts .code