playing-coffee/playing-coffee - Copy/roms/instr_timing/source/instr_timing.s

337 lines
7.5 KiB
ArmAsm

; Tests number of cycles taken by instructions
; except STOP, HALT, and illegals.
.include "shell.inc"
.include "timer.s"
.define saved_sp bss+0
.define instr bss+2 ; 3-byte instr + JP instr_end
.define instr_addr bss+8 ; JP instr_end
.redefine bss bss+11
main:
call init_timer
call test_timer
set_test 0
call test_main_ops
call test_cb_ops
jp tests_done
; Ensures timer works
test_timer:
call start_timer
call stop_timer
or a
ret z
set_test 2,"Timer doesn't work properly"
jp test_failed
; Tests main opcodes
test_main_ops:
ld l,0
- ld h,>op_times
ld a,(hl)
cp 0
call nz,@test_op
inc l
jr nz,-
ret
@test_op:
; Can't test the 8 RST instructions on devcart
ld a,l
cpl
and $C7
jr nz,+
ld a,(gb_id)
and gb_id_devcart
ret nz
+
; Test with flags set so that branches are
; not taken
ld a,l ; e = (l & 0x08 ? 0 : 0xFF)
and $08
add $F8
ld e,a
call @copy_and_exec
ld d,0
cp (hl)
jr z,+
ld d,a
call print_failed_opcode
+
; Time with branches not taken
ld a,e
cpl
ld e,a
call @copy_and_exec
ld h,>op_times_taken
cp (hl)
ret z
; If opcode already failed and timed the
; same again, avoid re-reporting.
cp d
ret z
call print_failed_opcode
ret
@copy_and_exec:
push de
push hl
ld h,>op_lens
ld c,(hl)
ld a,l
ld hl,instr
ld (hl+),a
dec c
jr z,@one_byte
ld a,0
dec c
jr z,@two_bytes
ld a,<instr_addr
ld (hl+),a
ld a,>instr_addr
@two_bytes:
ld (hl+),a
@one_byte:
ld a,e
call time_instruction
pop hl
pop de
ret
; Tests CB opcodes
test_cb_ops:
ld hl,cb_op_times
- ld a,(hl)
cp 0
call nz,@test_op_cb
inc l
jr nz,-
ret
@test_op_cb:
; Test with flags clear
ld e,$00
call @copy_and_exec_cb
cp (hl)
jr nz,+
; Test with flags set
ld e,$FF
call @copy_and_exec_cb
cp (hl)
jr nz,+
ret
+ print_str "CB "
call print_failed_opcode
ret
@copy_and_exec_cb:
push hl
; Copy instr to exec space
ld a,l
ld hl,instr+1
ld (hl+),a
ld a,$CB
ld (instr),a
call time_instruction
pop hl
ret
; Reports failed opcode
; L -> opcode
; A -> cycles it took
; (HL) -> cycles it should have taken
; Preserved: HL
print_failed_opcode:
; Print opcode
push af
ld a,l
call print_hex
ld a,':'
call print_char
pop af
; Print actual and correct times
call print_dec
ld a,'-'
call print_char
ld a,(hl)
call print_dec
ld a,' '
call print_char
; Remember that failure occurred
set_test 1
ret
; Times instruction.
; HL -> address of byte just after instruction
; A -> flags when executing instruction
; A <- number of cycles instruction took
time_instruction:
ld c,a
; Write JP instr_end to HL and instr_addr
ld a,$C3 ; JP
ld (hl+),a
ld (instr_addr),a
ld a,<instr_end
ld (instr_addr+1),a
ld (hl+),a
ld a,>instr_end
ld (instr_addr+2),a
ld (hl),a
; Save sp
ld (saved_sp),sp
; Set regs and stack contents
push bc
ld bc,instr_addr
ld de,instr_addr
ld hl,instr_addr
call start_timer
pop af
push hl
; Environment instruction executes in:
; 1 byte: OP
; 2 byte: OP 00
; 3 byte: OP instr_addr
; BC,DE,HL = instr_addr
; Stack has instr_addr pushed on it.
; Stack pointer can be trashed by instr.
; instr_addr contains JP instr_end, that
; can be trashed. Instructions which trash
; this don't execute it.
jp instr
instr_end: ; instruction jumps here when done
di
; Restore sp
ld sp,saved_sp
pop hl
ld sp,hl
call stop_timer
sub 24
ret
.section "page_aligned" align 256
; Instruction lengths of opcodes.
; 0 for instructions not timed.
op_lens:
.byte 1,3,1,1,1,1,2,1,3,1,1,1,1,1,2,1 ; 0
.byte 0,3,1,1,1,1,2,1,2,1,1,1,1,1,2,1 ; 1
.byte 2,3,1,1,1,1,2,1,2,1,1,1,1,1,2,1 ; 2
.byte 2,3,1,1,1,1,2,1,2,1,1,1,1,1,2,1 ; 3
.byte 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; 4
.byte 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; 5
.byte 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; 6
.byte 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; 7
.byte 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; 8
.byte 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; 9
.byte 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; A
.byte 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; B
.byte 1,1,3,3,3,1,2,1,1,1,3,0,3,3,2,1 ; C
.byte 1,1,3,0,3,1,2,1,1,1,3,0,3,0,2,1 ; D
.byte 2,1,1,0,0,1,2,1,2,1,3,0,0,0,2,1 ; E
.byte 2,1,1,1,0,1,2,1,2,1,3,1,0,0,2,1 ; F
; Timings for main opcodes
op_times:
.byte 1,3,2,2,1,1,2,1,5,2,2,2,1,1,2,1
.byte 0,3,2,2,1,1,2,1,3,2,2,2,1,1,2,1
.byte 2,3,2,2,1,1,2,1,2,2,2,2,1,1,2,1
.byte 2,3,2,2,3,3,3,1,2,2,2,2,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 2,2,2,2,2,2,0,2,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 2,3,3,4,3,4,2,4,2,4,3,0,3,6,2,4
.byte 2,3,3,0,3,4,2,4,2,4,3,0,3,0,2,4
.byte 3,3,2,0,0,4,2,4,4,1,4,0,0,0,2,4
.byte 3,3,2,1,0,4,2,4,3,2,4,1,0,0,2,4
; Timings when conditionals are taken
op_times_taken:
.byte 1,3,2,2,1,1,2,1,5,2,2,2,1,1,2,1
.byte 0,3,2,2,1,1,2,1,3,2,2,2,1,1,2,1
.byte 3,3,2,2,1,1,2,1,3,2,2,2,1,1,2,1
.byte 3,3,2,2,3,3,3,1,3,2,2,2,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 2,2,2,2,2,2,0,2,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1
.byte 5,3,4,4,6,4,2,4,5,4,4,0,6,6,2,4
.byte 5,3,4,0,6,4,2,4,5,4,4,0,6,0,2,4
.byte 3,3,2,0,0,4,2,4,4,1,4,0,0,0,2,4
.byte 3,3,2,1,0,4,2,4,3,2,4,1,0,0,2,4
; Timings for CB-prefixed opcodes
cb_op_times:
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2
.byte 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2
.byte 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2
.byte 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.byte 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
.ends
; RST handlers
.bank 0 slot 0
.org $00
jp instr_end
.org $08
jp instr_end
.org $10
jp instr_end
.org $18
jp instr_end
.org $20
jp instr_end
.org $28
jp instr_end
.org $30
jp instr_end
.org $38
jp instr_end