class ZXUtils::Benchmark

ZXUtils::Benchmark

Benchmarking utilities.

NOTE

Currently the code must be located at 0x8000.

Example:

require 'zxutils/benchmark'
require 'zxlib/basic'

include ZXLib

class BenchNeg16
    include Z80
    include Z80::TAP

    export test_neg16

    label_import        ZXLib::Sys
    import              ZXUtils::Benchmark, :bm, macros: true

    get_bench_result    calculate_benchmark_tstates(bm.counter, bm.tsframe, bm.frames, bm.idle, bm.adjustment)
    estimate_tsframes   estimate_tstates_per_interrupt(vars.udg, bm.interrup_vec, bm.forward, bm.tsframe, bm.idle)

    ns :test_neg16 do
                xor  a
                sub  l
                ld   l, a
                sbc  a, a
                sub  h
                ld   h, a
                ret
    end
end

benchmark = BenchNeg16.new 0x8000 # Note: this must be the 0x8000 address at the moment.
tsframe = benchmark['bm.tsframe']
program = Basic.parse_source <<-EOC
   1 DEF FN n(x)=x-(65536 AND x>=32768)
     DEF FN b(a,c)=USR #{benchmark['bm.bench']}: REM benchmark
     DEF FN t()=USR #{benchmark['bm.getset_tsframe']}+65536*PEEK #{tsframe+2}: REM get ts/frame
     DEF FN s(t)=USR #{benchmark['bm.getset_tsframe']}+65536*PEEK #{tsframe+2}: REM set ts/frame
     DEF FN i()=USR #{benchmark['bm.get_idle']}: REM idle
     DEF FN r()=USR #{benchmark[:get_bench_result]}: REM result
  10 LET counter=65535: LET frames=FN b(#{benchmark[:test_neg16]},counter)
     PRINT "Interrupts: ";frames
     PRINT "T-States: ";FN r()
     STOP
 100 REM Estimate T-States/interrupt
     LET adj=FN n(USR #{benchmark[:estimate_tsframes]}): LET idl=FN i(): 
     PRINT "Est. T-States/int.:";idl*512+adj;" (+-4)"
     INPUT "Is this ok? Y/n", a$: IF a$<>"n" AND a$<>"N" THEN RETURN
     INPUT "Enter value: ",tf: PRINT "T-States/int.:";FN s(tf)
     RETURN
9998 STOP
9999 CLEAR #{benchmark.org-1}: LOAD "benchmark"CODE: GO SUB 100: RUN
EOC
puts benchmark.debug
puts program.to_source escape_keywords: true
program.save_tap "testneg16", line: 9999
benchmark.save_tap "testneg16", name: "benchmark", append: true
puts "TAP: testneg16.tap:"
Z80::TAP.parse_file('testneg16.tap') do |hb|
    puts hb.to_s
end

See also: ZXUtils::Benchmark::Macros

Public Instance Methods

bench() click to toggle source

Benchmarks the tested routine. Provide a routine address and a counter. Returns a number of seconds (multiplied by the interrupt frequency - 50Hz) that have passed. This callback is to be used from the ZX Basic.

1 DEF FN b(a,c)=USR #{program['benchmark.bench']}
# File lib/zxutils/benchmark.rb, line 258
ns :bench do
                call fn_argn
                jr   C, start
    error_q     report_error_unless Z, 'Q Parameter error'
                call get_arg_bc
                ld   [routine], bc
                call fn_argn.seek_next
                jr   NZ, error_q.err
                call get_arg_bc
                ld   [counter], bc
end
get_adjustment() click to toggle source

Returns a signed integer. Convert with: LET x=x-(65536 AND x>=32768)

# File lib/zxutils/benchmark.rb, line 377
ns :get_adjustment do
                ld   bc, [adjustment]
                ret
end
get_frames() click to toggle source

Returns an unsigned integer

# File lib/zxutils/benchmark.rb, line 365
ns :get_frames do
                ld   bc, [frames]
                ret
end
get_idle() click to toggle source

Returns an unsigned integer

# File lib/zxutils/benchmark.rb, line 371
ns :get_idle do
                ld   bc, [idle]
                ret
end
getset_tsframe() click to toggle source

Returns a less significant 16-bit unsigned integer. Add 65536 to get the actual value.

# File lib/zxutils/benchmark.rb, line 348
ns :getset_tsframe do
                call fn_argn
                jr   C, only_get
                jr   NZ, only_get
                call get_arg_debc
                ld   a, d
                ora  a
                jp   NZ, overflow_err
                ld   [tsframe.bytes[0]], bc
                ld   a, e
                ld   [tsframe.bytes[2]], a
                ret
    only_get    ld   bc, [tsframe]
                ret
end
start() click to toggle source

A benchmark start entry for the machine-language.

Provide a routine and a counter address in the memory addressed by the appropriate labels.

# File lib/zxutils/benchmark.rb, line 273
with_saved :start, iy, :exx, hl, :exx, ret: true, use: vars do
                ld   hl, [routine]
                ld   [routine_a + 1], hl
                ld   hl, -1
                ld   [frames], hl
                inc  hl
                ld   [idle], hl
                ld   bc, [counter]
                di
                ld   [restore_sp+1], sp
                ld   sp, [vars.udg]
                ld   hl, inthandler
                ld   [forward+1], hl
                ld   a, interrup_vec>>8
                ld   i, a
                im2
                ei
                halt                 # 19 (accepting interrupt) + 10 (forward) + 73 (inthandler)
                                     #=102
    iterate     push bc              # 11
    routine_a   call nop_test        # 17+10+tested
                pop  bc              # 10
                dec  bc              #  6
                ld   a, c            #  4
                ora  b               #  4
                ld   hl, finale      # 10
                jp   NZ, iterate     # 10
                                     #=82+tested
                # Each full frame t-states: frames*(69888-102)
                                     # BC: 0
                ld   [forward+1], hl # 16
                ld   hl, forever     # 10
                                     #=26
    forever     inc  c               # 4
                126.times { nop }    # 126*4
                jp   (hl)            # 4
                                     #=512
                # Last frame: tsframe-(idle*512 + (pc==forever ? 0 : (pc - finale)*4) + 102 + 26)
    finale      ld   [idle], bc     # idle*512 + (pc==forever ? 0 : (pc - finale)*4) + 79
                pop  de              # de: pc
                xor  a
                ld   hl, forever
                sbc  hl, de          # hl: forever - pc
                jr   Z, no_adjust    # hl: 0 (pc == forever)
                jr   C, calc_idle   # (pc > forever)
                ld   hl, -10         # (pc < forever)
                jr   no_adjust
    calc_idle  ex   de, hl          # hl: pc
                ld   de, finale
                xor  a
                sbc  hl, de          # pc - finale
                add  hl, hl          # (pc - finale)*4
                add  hl, hl          # hl: adjust = (pc - finale)*4
    no_adjust   add  102 + 26
                adda_to h,l          # hl: adjust + 102 + 26
                ld   [adjustment], hl
    restore_sp  ld   sp, 0
                restore_rom_interrupt_handler
                ld   bc, [frames]    # return the number of frames that have passed
end