%if 0 Decompression speed benchmark by E. C. Masloch, 2018-2020, 2026 Usage of the works is permitted provided that this instrument is retained with the works, so that any entity that uses the works is notified of this instrument. DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. Links at build time to one of several depackers, which are under separate usage conditions. Refer to the individual depacker source files for the applicable usage conditions. %endif %include "lmacros3.mac" %ifndef _MAP %elifempty _MAP %else ; defined non-empty, str or non-str [map all _MAP] %endif defaulting [list -] %if __NASM_VERSION_ID__ >= 2000000h + 17 * 10000h numdef HAVE_NASM_NOTE, 1 %note Disable usesection detection %else numdef HAVE_NASM_NOTE, 0 %endif %imacro hiddenusesection 1.nolist usesection %1 %endmacro %unimacro usesection 1-2.nolist 0 %imacro usesection 1-2.nolist 0 %ifndef _SECTION_ADDED_%1 %error Section %1 not yet added %endif %if %2 %define _CURRENT_SECTION %define _CURRENT_SECTION_TYPE [section %1] %else %xdefine _CURRENT_SECTION %1 %xdefine _CURRENT_SECTION_TYPE SECTION_TYPE_%1 section %1 %endif %if _HAVE_NASM_NOTE %note usesection %1 %endif %endmacro %imacro noteincbin 2.nolist %strcat %%string _PAYLOAD_PREFIX_LST, %2 %if _HAVE_NASM_NOTE %note include listing file %%string %endif incbin %1 %endmacro [list +] numdef TIME, 1 numdef TIME_FORCE, 1 numdef TEST_PROGRAM, 1 numdef TEST_PROGRESS, 1 numdef INCLUDE_UNCOMPRESSED, 1 numdef INCLUDE_UNCOMPRESSED_IN_IMAGE, 1 numdef LZ4_8088, 0 numdef LZ4ECM, 0 numdef LZSA2, 0 numdef LZSA1, 0 numdef LZSADEFAULT, 0 %if (!!_LZ4_8088 + !!_LZ4ECM + !!_LZSA2 + !!_LZSA1 + !!_LZSADEFAULT) != 1 %fatal Exactly one compression method must be selected. %endif strdef PAYLOAD_FILE_COMPRESSED, "" strdef PAYLOAD_FILE_UNCOMPRESSED, "" %define MODULE bench %if _LZ4_8088 %define METHODNAME "lz4_8088" %endif %if _LZ4ECM %define METHODNAME "lz4ecm" %endif %if _LZSA2 || _LZSA1 %if _LZSA2 %define METHODNAME "lzsa2" %else %define METHODNAME "lzsa1" %endif %endif %if _LZSADEFAULT %define METHODNAME "lzsa" %endif struc XMSM .size_: resd 1 .srchdl: resw 1 .srcadr: resd 1 .dsthdl: resw 1 .dstadr: resd 1 endstruc cpu 8086 addsection _TEXT ..start: init0_start: mov bx, 8001h ; EXE mode, test B push ax %if _TEST_PROGRAM mov si, 81h xor dx, dx xor ax, ax xor di, di cmdline: .: lodsb cmp al, 9 je . cmp al, 32 je . cmp al, 13 je .end cmp al, '0' jb .notdigit cmp al, '9' ja .notdigit .digit: sub al, '0' mov cx, dx ; times 1 add dx, dx jc .invalid add dx, dx ; times 4 jc .invalid add dx, cx ; times 5 jc .invalid add dx, dx ; times 10 jc .invalid add dx, ax ; add in low digit jc .invalid jmp . .notdigit: cmp al, 'B' je .b cmp al, 'b' je .b %if _TIME cmp al, 'T' je .t cmp al, 't' je .t cmp al, 'L' je .l cmp al, 'l' je .l %endif .invalid: mov dx, -1 ; invalid jmp .end .b: or bh, 80h ; B mode. repeat depack, counter in command line jmp . %if _TIME .t: or bl, 80h jmp . .l: or bl, 40h lodsb cmp al, ':' je @F cmp al, '=' jne @FF @@: lodsb @@: cmp al, 32 je @BB cmp al, 9 je @BB cmp al, 13 je .invalid lea di, [si - 1] @@: lodsb cmp al, 13 je @F cmp al, 32 je @F cmp al, 9 jne @B @@: mov byte [si - 1], 0 cmp al, 13 jne . %endif .end: ; bx for ?whichtest %if _TIME push di ; for ?timefilename %endif push dx ; for ?repetitions %endif jmp init2_entry addsection PAYLOADCOMPRESSED payload: incbin _PAYLOAD_FILE_COMPRESSED .end: align 16, db 38 _fill 65536, 38, payload addsection PAYLOADUNCOMPRESSED payloaduncompressed: .: incbin _PAYLOAD_FILE_UNCOMPRESSED .end: .length equ .end - . align 16, db 38 usesection _TEXT init2_entry: ; ss:sp -> word amount repetitions, word ax ; bx = 1 if A mode test, ; 8001h if B mode test, ; 4001h if C mode test push bx ; push into ?whichtest lframe 0 %if _TIME lpar word, timefilename %endif lpar word, repetitions lpar word, whichtest lenter mov dx, word [bp + ?repetitions] cmp dx, -1 jne @F .cmdline_error: mov dx, init2_msg.cmdline_error mov cx, init2_msg.cmdline_error.length jmp init2_error @@: .b: test dx, dx jz .cmdline_error %if _TIME xor ax, ax mov ds, ax cli lvar dword, timestart push word [46Ch + 2] push word [46Ch] sti inc ax lvar word, amountruns push ax ; = 1 %endif call .depack ; Returns here after decompression. ; CY if error. jnc @F mov dx, init2_msg.initial_error mov cx, init2_msg.initial_error.length jmp init2_error_free @@: %if _INCLUDE_UNCOMPRESSED call checkdecompressed je @F mov dx, init2_msg.initial_error_2 mov cx, init2_msg.initial_error_2.length jmp init2_error_free @@: %endif %if _TEST_PROGRESS mov dx, init2_msg.progress.1 mov cx, init2_msg.progress.1.length .loop: push cs pop ds mov bx, 2 mov ah, 40h int 21h %else .loop: %endif dec word [bp + ?repetitions] jnz .test_b_skip_bound %if _TEST_PROGRESS push cs pop ds mov dx, init2_msg.progress.linebreak mov cx, init2_msg.progress.linebreak.length mov bx, 2 mov ah, 40h int 21h %endif jmp quit_with_time .test_b_skip_bound: %if _TIME inc word [bp + ?amountruns] %endif call .depack ; Returns here after decompression. ; CY if error. jc .test_b_fail %if _INCLUDE_UNCOMPRESSED call checkdecompressed je @F .test_b_fail: %if _TEST_PROGRESS push cs pop ds mov dx, init2_msg.progress.linebreak mov cx, init2_msg.progress.linebreak.length mov bx, 2 mov ah, 40h int 21h %endif mov dx, init2_msg.subsequent_error_2 mov cx, init2_msg.subsequent_error_2.length jmp init2_error_free @@: %endif %if _TEST_PROGRESS mov dx, init2_msg.progress.success mov cx, init2_msg.progress.success.length %endif jmp .loop .depack: mov ax, BUFFER push ax xor bx, bx push bx ; ?c_destination mov ax, PAYLOADCOMPRESSED push ax push bx ; ?c_source %if _LZ4_8088 extern _lz4_decompress call far _lz4_decompress xor dx, dx %elif _LZ4ECM extern _lz4_depack call far _lz4_depack %else extern _lzsa_depack call far _lzsa_depack %endif add sp, 8 depackedsize equ payloaduncompressed.length cmp dx, depackedsize >> 16 jne @F cmp ax, depackedsize & 0FFFFh @@: je @F stc @@: retn quit_with_time: %if _TIME %ifn _TIME_FORCE testopt [bp + ?whichtest], 80h jz .quit %endif xor ax, ax mov ds, ax cli lvar dword, timeend push word [46Ch + 2] push word [46Ch] sti mov bx, init2_msg.time.00 testopt [bp + ?whichtest], 40h jz .no_file mov ah, 62h int 21h mov ds, bx mov si, word [bp + ?timefilename] xor cx, cx ; create attributes mov dx, 0001_0001b ; create or open mov bx, 0110_0000_1_010_0_001b ; auto-commit, no int 24h, no inherit, ; DENY WRITE, write-only mov ax, 716Ch ; LFN open-create xor di, di ; alias hint stc int 21h jnc .got_file ; LFN call succeeded --> ; Early case for no-LFN-interface available. ; cmp ax, 1 ; je .try_sfn cmp ax, 7100h je .try_sfn ; Only now, we check whether the used drive supports LFNs. ; If it does, then we treat the error received as an ; actual error and cancel here. If not, the SFN function ; is called next as a fallback. ; ; We cannot rely on specific error returns like the ; expected 7100h CY (or 7100h CF-unchanged) or the similar ; 0001h CY (Invalid function) because no one agrees on what ; error code to use. ; ; dosemu returns 0003h (Path not found) on FATFS and ; redirected-non-dosemu drives. But may be changed so as to ; return 0059h (Function not supported on network). ; MSWindows 98SE returns 0002h (File not found) on ; DOS-redirected drives. ; DOSLFN with Fallback mode enabled supports the call (albeit ; limited to SFNs). ; ; To suss out what the error means, check LFN availability. ; ; Refer to https://github.com/stsp/dosemu2/issues/770 push ds push es push di push ax lframe none, nested lvar 34, fstype_buffer lvar 4, pathname_buffer lenter lodsw ; load first two bytes of pathname push ss pop ds mov dx, sp ; ds:dx -> ?pathname_buffer push ss pop es mov di, sp ; es:di -> ?pathname_buffer cmp ah, ':' ; starts with drive specifier ? je @F ; yes --> mov ah, 19h int 21h ; get current default drive add al, 'A' ; A: = 0, convert to drive letter mov ah, ':' ; drive specifier @@: stosw mov ax, '\' ; backslash and zero terminator stosw ; es:di -> ?fstype_buffer xor ax, ax mov cx, 34 >> 1 push di rep stosw ; initialise ?fstype_buffer to all zeros pop di ; -> ?fstype_buffer mov cx, 32 ; size of ?fstype_buffer xor bx, bx ; harden, initialise this mov ax, 71A0h ; get volume information stc int 21h jc @F ; if call not supported --> ; bx = FS flags test bh, 0100_0000b ; LFN interface available ? stc ; if no jz @F ; no --> clc ; is available @@: lleave pop ax ; (restore error code) pop di pop es pop ds jnc .error_file ; if LFN interface is available, actual error ; if LFN interface is not available, try SFN .try_sfn: mov si, word [bp + ?timefilename] xor cx, cx ; create attributes mov dx, 0001_0001b ; create or open mov bx, 0110_0000_1_010_0_001b ; auto-commit, no int 24h, no inherit, ; DENY WRITE, write-only mov ax, 6C00h ; Open-create stc int 21h jnc .got_file cmp ax, 1 je .try_old_open_or_create cmp ax, 6C00h jne .error_file .try_old_open_or_create: xchg ax, bx ; al = no inherit, DENY WRITE, write-only mov ah, 3Dh mov dx, si int 21h jnc .got_file mov ah, 3Ch int 21h jnc .got_file .error_file: mov bx, init2_msg.error_time_file jmp .no_file .got_file: push ax xchg bx, ax xor dx, dx xor cx, cx mov ax, 4202h int 21h ; to EOF push cs pop ds mov bx, init2_msg.time.00 call init2_error_disp_counted pop word [cs:init2_error_disp_counted.patchhandle] jmp @F .no_file: push cs pop ds call init2_error_disp_counted @@: mov ax, word [bp + ?timeend] mov dx, word [bp + ?timeend + 2] sub ax, word [bp + ?timestart] sbb dx, word [bp + ?timestart + 2] jnc .no_time_overflow add ax, 00B0h adc dx, 18h .no_time_overflow: lvar dword, timeelapsed push dx push ax xchg bx, dx mov cx, 10000 ; multiplier mul cx mov si, dx mov di, ax ; si:di = low word * 10000 xchg ax, bx ; ax = high word mul cx ; dx:ax = high word * 10000 add si, ax adc dx, 0 ; dx:si:di = ticks * 10000 xchg ax, dx ; ax:si:di xchg ax, si ; si:ax:di xchg ax, di ; si:di:ax = ticks * 10000 push bp xor bp, bp mov cx, 100 mul cx xchg di, ax ; dx:di = ticks * 10000 * 100 part, ax = mid word mov bx, dx ; bx:di mul cx add bx, ax adc dx, 0 ; dx:bx:di adc bp, 0 ; bp:dx:bx:di xchg ax, dx ; bp:ax:bx:di xchg ax, si ; bp:si:bx:di, ax = third high word mul cx add si, ax ; bp:si:bx:di = ticks * 10000 * 100 adc bp, dx ; cannot carry jc .time_carry mov cx, 182 xor dx, dx xchg ax, bp div cx xchg bp, ax xchg ax, si div cx xchg si, ax xchg ax, bx div cx xchg bx, ax xchg ax, di div cx ; si:bx:ax = ms/100 mov di, ax ; si:bx:di test bp, bp ; cannot be nonzero jz @F .time_carry: mov ax, -1 mov si, ax mov di, ax mov bx, ax @@: pop bp mov dx, bx ; (si:)dx:ax = ms/100 per loop test si, si jz .time_no_carry_dura .time_carry_dura: mov ax, -1 cwd .time_no_carry_dura: lvar dword, cms_per_loop; centi milliseconds push dx push ax mov cx, word [bp + ?amountruns] xor dx, dx xchg ax, si div cx xchg si, ax xchg ax, bx div cx xchg bx, ax xchg ax, di div cx ; si:bx:ax = ms/100 per run mov dx, bx ; (si:)dx:ax = ms/100 per run test si, si jz .time_no_carry_run .time_carry_run: mov ax, -1 cwd .time_no_carry_run: lvar dword, cms_per_run ; centi milliseconds push dx push ax push cs pop es mov di, init2_msg.time.0.patch - 1 mov si, init2_msg.time.0.decimalpoint mov bx, init2_msg.time.0.atleast call store_dec_dword_with_decimalpoint push cs pop ds mov bx, init2_msg.time.0 call init2_error_disp_counted pop di pop si push si push di ; get si:di = ?cms_per_run mov cx, 10000 ; get deciseconds xor dx, dx xchg ax, si div cx xchg ax, di div cx ; di:ax = deciseconds test di, di jnz .deciminutes cmp ax, 20 ; >= 2.0 seconds ? jb .nolargerunit .deciseconds: cmp ax, 600 ; >= 1.0 minutes ? jae .deciminutes cwd mov di, init2_msg.time.deciseconds.patch - 1 mov si, init2_msg.time.deciseconds.decimalpoint mov bx, init2_msg.time.deciseconds.atleast call store_dec_dword_with_decimalpoint add cx, init2_msg.time.deciseconds.length mov bx, init2_msg.time.deciseconds.prefix jmp .donelargerunit .deciminutes: mov cx, 60 xor dx, dx xchg ax, di ; dx:ax = high word, di = low word div cx xchg di, ax div cx mov dx, di ; dx:ax = deciminutes test dx, dx jnz .minutes cmp ax, 600 jae .minutes mov di, init2_msg.time.deciminutes.patch - 1 mov si, init2_msg.time.deciminutes.decimalpoint mov bx, init2_msg.time.deciminutes.atleast call store_dec_dword_with_decimalpoint add cx, init2_msg.time.deciminutes.length mov bx, init2_msg.time.deciminutes.prefix jmp .donelargerunit .minutes: mov cx, 10 xchg di, ax xchg ax, dx xor dx, dx ; dx:ax = high word, di = low word div cx xchg di, ax div cx mov dx, di ; dx:ax = minutes mov di, init2_msg.time.minutes.patch - 1 mov si, init2_msg.time.minutes.decimalpoint mov bx, init2_msg.time.minutes.atleast call store_dec_dword_with_decimalpoint add cx, init2_msg.time.minutes.length mov bx, init2_msg.time.minutes.prefix .donelargerunit: push cs pop ds call init2_error_disp_counted lea bx, [di - 1] mov byte [bx], cl call init2_error_disp_counted .nolargerunit: push cs pop ds mov bx, init2_msg.time.perrun call init2_error_disp_counted mov ax, word [bp + ?amountruns] call init2_error_decword mov bx, init2_msg.time.runs call init2_error_disp_counted mov si, word [bp + ?cms_per_loop + 2] mov di, word [bp + ?cms_per_loop] ; get si:di = ?cms_per_loop mov cx, 1000 ; get centiseconds xor dx, dx xchg ax, si div cx xchg si, ax xchg ax, di div cx ; si:di = centiseconds mov di, ax test si, si jnz .dura_notcentiseconds cmp ax, 10_00 jae .dura_notcentiseconds .dura_centiseconds: cwd mov di, init2_msg.dura.centiseconds.patch - 1 mov si, init2_msg.dura.centiseconds.decimalpoint mov bx, init2_msg.dura.centiseconds.atleast call store_dec_dword_with_decimalpoint add cx, init2_msg.dura.centiseconds.length jmp .dura_done .dura_notcentiseconds: mov cx, 10 ; get deciseconds xor dx, dx xchg ax, si div cx xchg ax, di div cx ; di:ax = deciseconds test di, di jnz .dura_deciminutes .dura_deciseconds: cmp ax, 600 ; >= 1.0 minutes ? jae .dura_deciminutes cwd mov di, init2_msg.dura.deciseconds.patch - 1 mov si, init2_msg.dura.deciseconds.decimalpoint mov bx, init2_msg.dura.deciseconds.atleast call store_dec_dword_with_decimalpoint add cx, init2_msg.dura.deciseconds.length jmp .dura_done .dura_deciminutes: mov cx, 60 xor dx, dx xchg ax, di ; dx:ax = high word, di = low word div cx xchg di, ax div cx mov dx, di ; dx:ax = deciminutes test dx, dx jnz .dura_minutes cmp ax, 600 jae .dura_minutes mov di, init2_msg.dura.deciminutes.patch - 1 mov si, init2_msg.dura.deciminutes.decimalpoint mov bx, init2_msg.dura.deciminutes.atleast call store_dec_dword_with_decimalpoint add cx, init2_msg.dura.deciminutes.length jmp .dura_done .dura_minutes: mov cx, 10 xchg di, ax xchg ax, dx xor dx, dx ; dx:ax = high word, di = low word div cx xchg di, ax div cx mov dx, di ; dx:ax = minutes mov di, init2_msg.dura.minutes.patch - 1 mov si, init2_msg.dura.minutes.decimalpoint mov bx, init2_msg.dura.minutes.atleast call store_dec_dword_with_decimalpoint add cx, init2_msg.dura.minutes.length .dura_done: mov bx, init2_msg.dura.prefix push cs pop ds call init2_error_disp_counted lea bx, [di - 1] mov byte [bx], cl call init2_error_disp_counted mov ax, word [bp + ?timeelapsed] mov dx, word [bp + ?timeelapsed + 2] call init2_error_decdword mov bx, init2_msg.time.3 call init2_error_disp_counted .quit: %endif mov ax, 4C00h int 21h %if _TIME ; INP: dx:ax = number ; es:di -> last byte of buffer (should be 11 bytes) ; es:si -> decimal point in buffer (skip if would write) ; es:bx -> part to always display at least ; OUT: es:di -> written number ; cx = written bytes (include decimal point if wrote past) store_dec_dword_with_decimalpoint: lframe near, nested lequ 10, bufferlen lvar ?bufferlen,buffer lenter lvar dword, dividend push dx push ax lvar dword, destination push es push di lvar word, destinationdecimalpoint push si lvar word, atleast push bx push ss pop es lea di, [bp + ?buffer + ?bufferlen - 1] mov bx, di std ; _AMD_ERRATUM_109_WORKAROUND does not apply ; dword [bp + ?dividend] = number to display mov cx, 10 ; divisor .loop_write: xor dx, dx push di mov di, 4 .loop_divide: mov ax, [bp + ?dividend - 2 + di] div cx mov word [bp + ?dividend - 2 + di], ax dec di dec di jnz .loop_divide ; dx = last remainder pop di xchg ax, dx ; ax = remainder (next digit) ; dword [bp + ?dividend] = result of div add al, '0' stosb cmp word [bp + ?dividend + 2], 0 jnz .loop_write cmp word [bp + ?dividend], 0 ; any more ? jnz .loop_write ; loop --> push ss pop ds mov si, bx ; -> last digit sub bx, di ; = length mov cx, bx ; = length les di, [bp + ?destination] .time.loop: cmp di, word [bp + ?destinationdecimalpoint] jne .time.next dec di .time.next: movsb loop .time.loop cld inc di ; -> numeric output cmp di, word [bp + ?atleast] jbe @F mov di, word [bp + ?atleast] @@: mov cx, word [bp + ?destination] inc cx sub cx, di ; = length of numeric output lleave lret %endif %if _INCLUDE_UNCOMPRESSED ; INP: BUFFER => data, length depackedsize ; PAYLOADUNCOMPRESSED => to check ; OUT: ZR if matching ; NZ if mismatching ; CHG: es, ds, di, si, ax, bx, cx ; STT: UP checkdecompressed: mov ax, BUFFER mov es, ax mov ax, PAYLOADUNCOMPRESSED mov ds, ax xor si, si xor di, di mov cx, depackedsize repe cmpsb retn %endif init2_error_free: init2_error: push cs pop ds mov bx, 2 mov ah, 40h int 21h mov ax, 4CFFh int 21h lleave ctx %if _TIME init2_error_disp_ax_hex: ; ax xchg al,ah call init2_error_disp_al_hex ; display former ah xchg al,ah ; and fall trough for al init2_error_disp_al_hex: ; al push cx mov cl,4 ror al,cl call init2_error_disp_al_lownibble_hex ; display former high-nibble rol al,cl pop cx ; and fall trough for low-nibble init2_error_disp_al_lownibble_hex: push ax ; save ax for call return and al,00001111b ; high nibble must be zero add al,'0' ; if number is 0-9, now it's the correct character cmp al,'9' jna .decimalnum ; if we get decimal number with this, ok --> add al,7 ; otherwise, add 7 and we are inside our alphabet .decimalnum: call init2_error_disp_al pop ax retn init2_error_disp_al: push dx push cx push bx push ax mov dx, sp push ds push ss pop ds mov cx, 1 mov bx, 2 mov ah, 40h int 21h pop ds pop ax pop bx pop cx pop dx retn %endif init2_disp_al: push dx push ax mov dl, al mov ah, 2 int 21h pop ax pop dx retn %if _TIME init2_error_disp_counted: push ax push bx push cx push dx xor cx, cx mov cl, [bx] lea dx, [bx + 1] mov ah, 40h mov bx, 2 int 21h %if _TIME mov bx, -1 .patchhandle: equ $ - 2 cmp bx, -1 je @F mov ah, 40h int 21h @@: %endif pop dx pop cx pop bx pop ax retn init2_error_decword: push dx xor dx, dx call init2_error_decdword pop dx retn init2_error_decdword: push cx xor cx, cx call init2_error_dec_dword_minwidth pop cx retn ; Dump dword as decimal number string ; ; INP: dx:ax = dword ; cx = minimum width (<= 1 for none, must be < 10) ; es:di -> where to store ; OUT: es:di -> behind variable-length string ; CHG: - ; STT: UP init2_error_dec_dword_minwidth: lframe near lequ 12, bufferlen lvar ?bufferlen,buffer lenter lvar dword, dividend push dx push ax dec cx lvar word, minwidth push cx inc cx push ax push bx push cx push dx push si push di push es push ss pop es lea di, [bp + ?buffer + ?bufferlen - 1] mov bx, di std ; _AMD_ERRATUM_109_WORKAROUND does not apply ; dword [bp + ?dividend] = number to display mov cx, 10 ; divisor .loop_write: xor dx, dx push di mov di, 4 .loop_divide: mov ax, [bp + ?dividend - 2 + di] div cx mov word [bp + ?dividend - 2 + di], ax dec di dec di jnz .loop_divide ; dx = last remainder pop di xchg ax, dx ; ax = remainder (next digit) ; dword [bp + ?dividend] = result of div add al, '0' stosb dec word [bp + ?minwidth] jns .loop_write cmp word [bp + ?dividend + 2], 0 jnz .loop_write cmp word [bp + ?dividend], 0 ; any more ? jnz .loop_write ; loop --> cld sub bx, di mov cx, bx push ds push ss pop ds mov bx, di mov byte [bx], cl call init2_error_disp_counted pop ds pop es pop di pop si pop dx pop cx pop bx pop ax lleave retn %endif ; Display number in ax decimal ; ; INP: ax = number ; OUT: displayed using Int21.02 ; CHG: none init2_disp_ax_dec: ; ax (no leading zeros) push bx xor bx, bx .pushax: push dx push ax or bl, bl jz .nobl sub bl, 5 neg bl .nobl: push cx mov cx, 10000 call .divide_out mov cx, 1000 call .divide_out mov cx, 100 call .divide_out mov cl, 10 call .divide_out ; (Divisor 1 is useless) add al, '0' call init2_disp_al pop cx pop ax pop dx pop bx ; Caller's register retn ; INP: ax = number ; cx = divisor ; OUT: ax = remainder of operation ; result displayed .divide_out: push dx xor dx, dx div cx ; 0:ax / cx push dx ; remainder dec bl jnz .nobl2 or bh, 1 .nobl2: or bh, al jz .leadingzero add al, '0' call init2_disp_al ; display result .leadingzero: pop ax ; remainder pop dx retn init2_msg: %if _TIME .error_time_file: counted "Note: Unable to append to specified time log file!",13,10,"Time: " .time.00: counted "Time: " .time.0: db @F - ($ + 1) times 7 db 32 .time.0.atleast: db "0" .time.0.decimalpoint: db "." db "00" ; good enough for 99_999_999.99ms (27h) ; though we only use up to 42_949_672.95ms (11h) as a 32-bit ms/100 .time.0.patch: db "ms" @@: db 0 times 8 db 32 .time.deciseconds.atleast: db "0" .time.deciseconds.decimalpoint: db "." db "0" .time.deciseconds.patch: .time.deciseconds.length: equ @F - $ db "s)" @@: db 0 times 8 db 32 .time.deciminutes.atleast: db "0" .time.deciminutes.decimalpoint: db "." .time.minutes.decimalpoint: equ -1 .time.minutes.atleast: db "0" .time.deciminutes.patch: .time.minutes.patch: .time.deciminutes.length: equ @F - $ .time.minutes.length: equ @F - $ db "min)" @@: .time.deciseconds.prefix: ; counted " (" .time.deciminutes.prefix: ; counted " (" .time.minutes.prefix: counted " (" .dura.prefix: counted ", sum " db 0 times 7 db 32 .dura.centiseconds.atleast: db "0" .dura.centiseconds.decimalpoint:db "." db "00" .dura.centiseconds.patch: .dura.centiseconds.length: equ @F - $ db "s=" @@: db 0 times 8 db 32 .dura.deciseconds.atleast: db "0" .dura.deciseconds.decimalpoint: db "." db "0" .dura.deciseconds.patch: .dura.deciseconds.length: equ @F - $ db "s=" @@: db 0 times 8 db 32 .dura.deciminutes.atleast: db "0" .dura.deciminutes.decimalpoint: db "." .dura.minutes.decimalpoint: equ -1 .dura.minutes.atleast: db "0" .dura.deciminutes.patch: .dura.minutes.patch: .dura.deciminutes.length: equ @F - $ .dura.minutes.length: equ @F - $ db "min=" @@: .time.perrun: counted "/run, " .time.runs: counted " runs" @@: %strlen LENGTH METHODNAME %if LENGTH <= 8 %assign LENGTH 8 - LENGTH %else %aasign LENGTH 0 %endif .time.3: db @F - ($ + 1) db "t " times LENGTH db 32 db METHODNAME strdef APPEND_NAME, "" db _APPEND_NAME strdef APPEND_INCLUDE, "" %ifnidni _APPEND_INCLUDE, "" incbin _APPEND_INCLUDE %endif db 13,10 @@: %endif %if _TEST_PROGRESS .progress.1: db "Info: 1" .progress.1.length: equ $ - .progress.1 .progress.success: db "S" .progress.success.length: equ $ - .progress.success .progress.linebreak: db 13,10 .progress.linebreak.length: equ $ - .progress.linebreak %endif .initial_error: db "Error: Test program failed to decompress with full buffer." .linebreak: db 13,10 .initial_error.length: equ $ - .initial_error .linebreak.length: equ $ - .linebreak %if _INCLUDE_UNCOMPRESSED .initial_error_2: db "Error: Test program decompressed wrongly with full buffer.",13,10 .initial_error_2.length: equ $ - .initial_error_2 .subsequent_error_2: db "Error: Test program decompressed wrongly during test.",13,10 .subsequent_error_2.length: equ $ - .subsequent_error_2 %endif .cmdline_error: db "Error: Invalid command line input.",13,10 .cmdline_error.length: equ $ - .cmdline_error .error_c_stdout: .error_stdout: db "0" db 13,10 .error_c_stdout.length: equ $ - .error_c_stdout .error_stdout.length: equ $ - .error_stdout addsection BUFFER resb 65536 addsection STACK, class=stack stack stack: resb 4096 .top: