%if 0 8086 Assembly LZ4 depacker by C. Masloch, 2018-2022 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. MODIFIED to never normalise pointers! %endif %include "lmacros3.mac" %ifndef _MAP %elifempty _MAP %else ; defined non-empty, str or non-str [map all _MAP] %endif defaulting numdef DEBUG0 ; use errordata to generate an error code numdef DEBUG1 ; dump_stack_frame after an error occurred numdef DEBUG2 ; dump_stack_frame before blz_depack_safe call numdef DEBUG3 ; dump_stack_frame at start of blz_depack_safe numdef ALLOW_OVERLAPPING, 0 ; allow overlapping src and dst numdef IMAGE_EXE, 1 numdef DEVICE, 0 numdef BRIEFLZ, 0 numdef LZ4, 1 numdef SNAPPY, 0 numdef EXODECR, 0 numdef X, 0 numdef HEATSHRINK, 0 numdef LZD, 0 numdef LZO, 0 numdef LZSA2, 0 numdef APL, 0 numdef BZP, 0 %if (!!_BRIEFLZ + !!_LZ4 + !!_SNAPPY + !!_EXODECR + !!_X + !!_HEATSHRINK \ + !!_LZD + !!_LZO + !!_LZSA2 + !!_APL + !!_BZP) != 1 %fatal Exactly one compression method must be selected. %endif %assign ADDITIONAL_MEMORY 0 numdef PAYLOAD_KERNEL_MAX_PARAS, 0, 0 %if _PAYLOAD_KERNEL_MAX_PARAS && ! _IMAGE_EXE %error Kernel mode max paras requires building dual-mode executable %endif numdef COUNTER, 0, 32 %if (_COUNTER - 1) & _COUNTER %error COUNTER must be a power of two %endif cpu 8086 section _TEXT init1_start: %if _DEBUG0 || _DEBUG1 || _DEBUG2 || _DEBUG3 || _COUNTER disp_error.loop: call disp_al disp_error: lodsb test al, al jnz .loop retn %if _DEBUG0 || _DEBUG1 || _DEBUG2 || _DEBUG3 disp_ax_hex: ; ax xchg al,ah call disp_al_hex ; display former ah xchg al,ah ; and fall trough for al disp_al_hex: ; al push cx mov cl,4 ror al,cl call disp_al_lownibble_hex ; display former high-nibble rol al,cl pop cx ; and fall trough for low-nibble 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 disp_al pop ax retn %endif disp_al: push ax push bx push bp mov ah, 0Eh mov bx, 7 %if _IMAGE_EXE || _DEVICE push dx test byte [bp - 2], 1 | 2 jz .display_kernel_mode xchg dx, ax ; dl = input al mov ah, 02h int 21h db __TEST_IMM16 ; (skip int) .display_kernel_mode: %endif int 10h %if _IMAGE_EXE pop dx %endif pop bp pop bx pop ax disp_error.ret: retn %endif %assign NEED_NORMALISE_POINTER_WITH_DISPLACEMENT 1 %assign CHECK_POINTERS_VARIABLE_SRC 1 %assign CHECK_POINTERS_VARIABLE_DST 1 lz4_flg_version_mask: equ 1100_0000b lz4_flg_version: equ 0100_0000b lz4_flg_b_indep: equ 0010_0000b lz4_flg_b_checksum: equ 0001_0000b lz4_flg_c_size: equ 0000_1000b lz4_flg_c_checksum: equ 0000_0100b lz4_flg_reserved: equ 0000_0010b lz4_flg_dict_id: equ 0000_0001b lz4_bd_reserved_mask: equ 1000_1111b ; BD block max size is not used by us ; INP: dword on stack -> destination ; dword on stack -> source ; 1_0000h = length of destination ; 1_0000h = length of source ; OUT: NC if success, ; dx:ax = decompressed size ; CY if error, ; bx = ?errordata (if _DEBUG0) ; dx:ax = 0 ; CHG: (ax, bx, dx), cx, es, si, di ; STT: UP, preserves ds ; ; Note: The input pointers need not be normalised yet. ; Normalised means that the offset part is below 16. ; Optimisation: Normalised now means an offset ; below 8000h. %ifnidni __OUTPUT_FORMAT__, bin global _lz4_depack %endif _lz4_depack: depack: lframe far lpar dword, c_destination lpar dword, c_source lpar_return xor cx, cx mov dx, 1 mov bx, 1 mov ax, -1 %if _IMAGE_EXE || _DEBUG0 || _COUNTER lenter early %if _IMAGE_EXE lvar word, exemode ; must be bp - 2! push bx %if ?exemode != -2 %error exemode variable must be directly below bp %endif %endif %if _DEBUG0 || _COUNTER xor bx, bx %endif %if _DEBUG0 lvar word, errordata push bx %endif %endif lvar word, token_and_counter lequ ?token_and_counter, token lequ ?token_and_counter + 1, counter %if _COUNTER push bx ; initialise counter (high byte) to zero %endif lvar dword, src_behind_block lvar word, frame_descriptor_flg_and_bd lenter lvar word, c_preserve_ds push ds lds si, [bp + ?c_source] les di, [bp + ?c_destination] lvar dword, src push ds push si lvar dword, original_dst push es push di lvar dword, dst push es push di lvar dword, length_of_source push dx push cx %if _PAYLOAD_KERNEL_MAX_PARAS cmp ax, -1 ; no maximum specified ? je @FF ; retain -1 in ax --> test di, di ; do we need an additional paragraph ? jz @F ; no --> inc ax ; es + ax => paragraph after necessary part @@: mov dx, es add ax, dx ; => paragraph after necessary part ; If the normalised destination pointer's segment grows ; to this segment then enough has been decompressed. d0 mov byte [bp + ?errordata], 40h jc .error ; should not carry @@: lvar word, dst_max_segment push ax %endif xor cx, cx mov bx, 1 lvar dword, length_of_destination push bx ; push into [bp + ?length_of_destination + 2] push cx ; push into [bp + ?length_of_destination] %if _ALLOW_OVERLAPPING call check_pointers_not_overlapping ; Note: We initially check here that the write pointer is ; low enough, ie below-or-equal the read pointer. ; Doing this check here (as well as after any ; copied match) allows us to drop the check done ; after moving literal bytes. d0 mov byte [bp + ?errordata], 1Bh jc .error %endif ; .loop_frame: xor cx, cx cmp word [bp + ?length_of_source + 2], cx jne @F cmp word [bp + ?length_of_source], cx jne @F .loop_frame: .end: db __TEST_IMM8 ; (NC) .error: stc %if _COUNTER lahf mov al, 13 call disp_al mov al, 10 call disp_al sahf %endif mov ax, 0 mov dx, ax jc .ret push word [bp + ?original_dst + 2] push word [bp + ?original_dst] call pointer_to_linear mov bx, dx xchg cx, ax ; bx:cx = original dst linear push word [bp + ?dst + 2] push word [bp + ?dst] call pointer_to_linear ; dx:ax = dst linear sub ax, cx sbb dx, bx ; dx:ax = dst - original dst ; = size of destination clc .ret: d0 mov bx, word [bp + ?errordata] mov ds, word [bp + ?c_preserve_ds] lleave code lret @@: mov cl, 4 call read_le_data_src d0 mov byte [bp + ?errordata], 1 jc .error cmp dx, 184Dh ; signature match ? d0 mov byte [bp + ?errordata], 2 jne .error ; no --> cmp ax, 2204h ; usual frame ? je @F ; yes --> and al, ~0Fh cmp ax, 2A50h ; skippable frame ? d0 mov byte [bp + ?errordata], 3 jne .error ; no --> ; cx = 4 still here call read_le_data_src ; read length of skippable frame d0 mov byte [bp + ?errordata], 4 jc .error push ds push si mov cx, ax mov bx, dx ; displacement = skippable frame length call normalise_pointer_with_displacement_bxcx pop word [bp + ?src] pop word [bp + ?src + 2] ; ?src += length d0 mov byte [bp + ?errordata], 1Dh jc .error sub word [bp + ?length_of_source], ax sbb word [bp + ?length_of_source + 2], dx ; ?length_of_source -= length d0 mov byte [bp + ?errordata], 5 jc .error jmp .loop_frame @@: mov cl, 2 call read_le_data_src ; read FLG (low byte), BD (high byte) d0 mov byte [bp + ?errordata], 6 .error_CY_1: jc .error mov word [bp + ?frame_descriptor_flg_and_bd], ax mov bx, ax and bl, lz4_flg_version_mask cmp bl, lz4_flg_version ; version correct ? d0 mov byte [bp + ?errordata], 7 .error_NZ_1: jne .error ; no --> test ax, (lz4_bd_reserved_mask << 8) \ | lz4_flg_reserved | lz4_flg_dict_id ; unsupported bits set? d0 mov byte [bp + ?errordata], 8 jnz .error_NZ_1 ; yes --> ; not checked: lz4_flg_b_indep (linked is always supported), ; lz4_flg_b_checksum, lz4_flg_c_checksum (both checked later) test al, lz4_flg_c_size ; content size field present ? jz @F ; no --> mov cl, 8 call read_le_data_src ; (skip content size field) d0 mov byte [bp + ?errordata], 9 jc .error_CY_1 @@: mov cl, 1 call read_le_data_src ; (skip header checksum field) d0 mov byte [bp + ?errordata], 0Ah jc .error_CY_1 .loop_block: %if _COUNTER mov al, '#' call disp_al %endif mov cx, 4 call read_le_data_src ; read block size field ; leaves ds:si -> source d0 mov byte [bp + ?errordata], 0Bh jc .error_CY_1 test dx, dx ; nonzero ? jnz @FF test ax, ax jnz @FF ; yes --> test byte [bp + ?frame_descriptor_flg_and_bd], lz4_flg_c_checksum jz @F ; cx = 4 already call read_le_data_src ; (skip c checksum field) d0 mov byte [bp + ?errordata], 0Ch .error_CY_2: jc .error_CY_1 @@: jmp .loop_frame ; next frame, if any --> @@: push ds push si ; -> source mov bx, dx and bh, ~80h ; clear "uncompressed" flag mov cx, ax ; bx:cx = block size call normalise_pointer_with_displacement_bxcx ; -> source after block pop word [bp + ?src_behind_block] pop word [bp + ?src_behind_block + 2] d0 mov byte [bp + ?errordata], 1Eh jc .error test dh, 80h ; is it uncompressed ? jz .compressed ; no --> .uncompressed: xchg bx, dx ; get size without "uncompressed" flag call lz4_copy_literal_or_uncompressed d0 mov byte [bp + ?errordata], 0Dh jc .error_CY_2 .end_block: push word [bp + ?src_behind_block + 2] push word [bp + ?src_behind_block] call pointer_to_linear mov cx, ax mov bx, dx ; bx:cx = linear ?src_behind_block push word [bp + ?src + 2] push word [bp + ?src] call pointer_to_linear ; dx:ax = linear ?src sub cx, ax sbb bx, dx ; ?src_behind_block - ?src ; = amount to skip d0 mov byte [bp + ?errordata], 0Fh jc .error_CY_2 sub word [bp + ?length_of_source], cx sbb word [bp + ?length_of_source + 2], bx d0 mov byte [bp + ?errordata], 10h .error_CY_3: jc .error_CY_2 ; not enough data to skip --> push word [bp + ?src_behind_block + 2] push word [bp + ?src_behind_block] pop word [bp + ?src] pop word [bp + ?src + 2] ; set ?src -> behind block test byte [bp + ?frame_descriptor_flg_and_bd], lz4_flg_b_checksum jz @F ; no block checksum --> mov cx, 4 call read_le_data_src ; (skip block checksum field) d0 mov byte [bp + ?errordata], 0Eh jc .error_CY_3 @@: jmp .loop_block .compressed: .loop_sequence: %if _COUNTER inc byte [bp + ?counter] test byte [bp + ?counter], _COUNTER - 1 jnz @F mov al, '.' call disp_al @@: %endif mov bx, .lodsb call lz4_func_bx_with_src ; load token d0 mov byte [bp + ?errordata], 11h jc .error_CY_3 mov byte [bp + ?token], al mov cl, 4 shr al, cl call lz4_get_length ; dx:cx = length of literals d0 mov byte [bp + ?errordata], 12h jc .error_CY_3 mov ax, cx ; dx:ax = length of literals (may be 0) call lz4_copy_literal_or_uncompressed ; copy over the literals d0 mov byte [bp + ?errordata], 13h jc .error_CY_3 call lz4_check_behind_block_higher_than_src ; do we have more data in this block ? jc .end_sequence ; no --> mov bx, .lodsw call lz4_func_bx_with_src ; load match offset d0 mov byte [bp + ?errordata], 14h .error_CY_4: jc .error_CY_3 test ax, ax ; offset is 0 ? d0 mov byte [bp + ?errordata], 15h stc jz .error_CY_4 ; yes, error --> mov cx, ax neg cx ; -1 .. -65535 mov bx, -1 ; sign-extend (always negative sense) push word [bp + ?dst + 2] push word [bp + ?dst] call normalise_pointer_with_displacement_bxcx pop si pop ds ; ds:si -> source of matching d0 mov byte [bp + ?errordata], 1Fh jnc .error mov ax, ds ; is source of matching within ?dst ? cmp word [bp + ?original_dst + 2], ax d0 mov byte [bp + ?errordata], 16h .error_A_1: ja .error jb @F cmp word [bp + ?original_dst], si d0 mov byte [bp + ?errordata], 17h ja .error_A_1 ; no, ?original_dst is above it --> @@: mov al, byte [bp + ?token] and al, 0Fh ; get match length from token call lz4_get_length ; dx:cx = length of match - 4 d0 mov byte [bp + ?errordata], 18h jc .error_CY_4 mov ax, cx ; dx:ax = length of match - 4 add ax, 4 adc dx, 0 ; dx:ax = length of match d0 mov byte [bp + ?errordata], 19h jc .error_CY_4 call lz4_copy_data ; give ?dst -> dest, ds:si -> source ; returns: ?dst incremented, ds:si -> after match source d0 mov byte [bp + ?errordata], 1Ah jc .error_CY_4 %if _ALLOW_OVERLAPPING call check_pointers_not_overlapping d0 mov byte [bp + ?errordata], 1Ch jc .error_CY_4 %endif .next_sequence: call lz4_check_behind_block_higher_than_src jnc .loop_sequence ; process next sequence --> .end_sequence: jmp .end_block ; INP: ds:si -> source data ; ?length_of_source ; OUT: CY if error (source buffer too small) ; NC if success, ; al = value read ; ds:si incremented (NOT normalised) ; ?length_of_source decremented .lodsb: sub word [bp + ?length_of_source], 1 sbb word [bp + ?length_of_source + 2], 0 jb .retn lodsb .retn: retn ; INP: ds:si -> source data ; ?length_of_source ; OUT: CY if error (source buffer too small) ; NC if success, ; ax = value read ; ds:si incremented (NOT normalised) ; ?length_of_source decremented .lodsw: sub word [bp + ?length_of_source], 2 sbb word [bp + ?length_of_source + 2], 0 jb .retn lodsw retn ; INP: ?src_behind_block -> block end (normalised) ; ?src -> current address in source buffer ; OUT: CY if ?src is >= (above-or-equal) ?src_behind_block ; NC else (?src is below ?src_behind_block, more data) ; CHG: ax, dx ; ; Note: Although ?src should always equal ?src_behind_block ; if CY here, we actually check for above-or-equal. lz4_check_behind_block_higher_than_src: mov dx, word [bp + ?src_behind_block + 2] mov ax, word [bp + ?src_behind_block] ; dx:ax -> behind block cmp word [bp + ?src + 2], dx ; compare segment ja .error ; ?src > dx:ax --> jb .success ; ?src < dx:ax --> cmp word [bp + ?src], ax ; segment is same, compare offset jae .error ; ?src >= dx:ax --> .success: db __TEST_IMM8 ; (NC) .error: stc retn ; INP: ?dst -> destination (normalised) ; ?src -> source (normalised) ; dx:ax = how long the literal data is (0 is valid) ; OUT: ?dst incremented (normalised) ; ?src incremented (normalised) ; ?length_of_destination shortened ; ?length_of_source shortened ; CY if error (either buffer too small) ; NC if success ; CHG: cx, dx, ax, es, di, ds, si lz4_copy_literal_or_uncompressed: lds si, [bp + ?src] sub word [bp + ?length_of_source], ax sbb word [bp + ?length_of_source + 2], dx ; enough data exists ? jb .return ; no --> (CY) call lz4_copy_data ; CY/NC depending on status mov word [bp + ?src], si mov word [bp + ?src + 2], ds ; (CF preserved) .return: retn ; INP: ?dst -> destination (normalised) ; ds:si -> source (normalised) ; dx:ax = how long the data is (0 is valid) ; OUT: ?dst incremented (normalised) ; ds:si incremented (normalised) ; ?length_of_destination shortened ; CY if error (buffer too small) ; NC if success ; Instead of returning, this may jump to depack.end ; if the destination segment grows up to the value ; stored in the ?dst_max_segment variable. It is ; assumed that this will use the stack frame to leave ; the function, therefore discarding any of the stack ; contents between the frame and the stack top. ; CHG: cx, dx, ax, es, di, ds, si lz4_copy_data: d0 inc byte [bp + ?errordata + 1] sub word [bp + ?length_of_destination], ax sbb word [bp + ?length_of_destination + 2], dx ; enough space left ? jb .error ; no --> les di, [bp + ?dst] .loop: mov cx, 16 * 1024 - 16 ; block size ; If both pointers are normalised, moving 0FFF0h bytes is ; valid and results in a maximum offset of 0FFFFh in either. ; Optimisation: Copy at most 3FF0h bytes. This allows to ; normalise the pointers less often. test dx, dx ; >= 1_0000h ? jnz @F ; yes, full block --> test ax, ax ; == 0 ? jz .end ; yes, done --> cmp ax, cx ; can move in one (last) block ? jbe .last ; yes --> @@: ; no, move one block and continue sub ax, cx sbb dx, 0 ; left over remaining jmp .copy .last: xchg cx, ax ; cx = remaining length xor ax, ax ; no more remaining .copy: rep movsb ; move one block (full or partial) jmp .loop .end: mov word [bp + ?dst], di mov word [bp + ?dst + 2], es %if _PAYLOAD_KERNEL_MAX_PARAS mov ax, es cmp ax, word [bp + ?dst_max_segment] jae depack.end %endif db __TEST_IMM8 ; (NC) .error: stc retn ; INP: ?src -> source data ; bx = function to call ; OUT: ds:si = ?src -> behind data (normalised) ; CY if error (buffer too small) ; NC if success ; (CF is passed from what the function at bx returns) ; ; Protocol of function in bx called by this: ; INP: ds:si -> source data ; ?length_of_source ; (may take more inputs depending on function) ; OUT: ds:si incremented (NOT normalised) ; ?length_of_source decremented ; CY if error (buffer too small or other error) ; NC if success ; (may give more outputs depending on function) lz4_func_bx_with_src: lds si, [bp + ?src] call bx pushf mov word [bp + ?src], si mov word [bp + ?src + 2], ds popf retn ; INP: ?src -> source data ; ?length_of_source ; al = first field data (0 to 15) ; OUT: dx:cx = length value ; ?src incremented ; ?length_of_source decremented ; CY if error (too high or out of source data) ; NC if success ; CHG: ax, bx lz4_get_length: push ds push si xor cx, cx xor dx, dx ; dx:cx = length field mov ah, 15 ; first byte maximum value .loop: add cl, al adc ch, 0 adc dx, 0 ; add in this byte jc .error ; if too high --> cmp al, ah ; is this the maximum value ? jne .end ; no --> mov bx, depack.lodsb call lz4_func_bx_with_src ; load next byte jc .error mov ah, 255 ; next maximum value is 255 jmp .loop ; --> .end: db __TEST_IMM8 ; (NC) .error: stc pop si pop ds retn ; INP: ?src -> source data (normalised) ; cx = size to read, 1 to 4 or higher (at most 0FFF0h) ; ?length_of_source ; OUT: If cx >= 4, ; dx:ax = value read as little-endian ; If cx == 3, ; dl:ax = value read ; If cx == 2, ; ax = value read ; If cx == 1, ; al = value read ; If cx == 0, ; invalid, CY ; ds:si = ?src = incremented source (normalised) ; ?length_of_source decremented ; CY if error (source buffer too small, or cx zero) ; NC if success ; CHG: ds, si, dx, ax ; ; Note: Always reads four bytes at INP:ds:si -> data. read_le_data_src: jcxz .error sub word [bp + ?length_of_source], cx sbb word [bp + ?length_of_source + 2], 0 jb .error lds si, [bp + ?src] lodsw xchg ax, dx ; dx = low word (first) lodsw ; ax = high word (second) xchg ax, dx ; fix order, dx:ax = dword sub si, 4 add si, cx mov word [bp + ?src], si mov word [bp + ?src + 2], ds db __TEST_IMM8 ; (NC) .error: stc retn %if _ALLOW_OVERLAPPING ; INP: ?src, ?dst ; OUT: CY if error (?src < ?dst) ; NC if success ; CHG: ax, bx, cx, dx check_pointers_not_overlapping: %if CHECK_POINTERS_VARIABLE_DST push word [bp + ?dst + 2] push word [bp + ?dst] %else push es push di %endif call pointer_to_linear xchg cx, ax xchg bx, dx ; bx:cx = linear ?dst after write %if CHECK_POINTERS_VARIABLE_SRC push word [bp + ?src + 2] push word [bp + ?src] %else push ds push si %endif call pointer_to_linear ; dx:ax = linear ?src before next read cmp dx, bx ; ?src >= ?dst ? jne @F cmp ax, cx @@: ; (CY) if error (src < dst) ; (NC) if no error retn %endif ; This leaves the lframe context created within the ; specific depacker's file. The above function ; check_pointers_not_overlapping uses the frame. lleave ctx ; INP: ds:si = pointer ; es:di = pointer ; OUT: ds:si normalised ; es:di normalised normalise_both_pointers: push es push di pop di pop es normalise_dssi_pointer: push ds push si pop si pop ds retn ; INP: word [ss:sp + 2] = segment ; word [ss:sp] = offset ; ; Note: Does not work correctly with pointers that point to ; a HMA location. Do not use then! ; REM: Normalises offset to below 8000h, as an optimisation. normalise_pointer: %if NEED_NORMALISE_POINTER_WITH_DISPLACEMENT lframe near lpar word, segment lpar word, offset lpar_return lenter cmp word [bp + ?offset], 7FFFh jbe .ret push bx push cx xor bx, bx xor cx, cx push word [bp + ?segment] push word [bp + ?offset] call normalise_pointer_with_displacement_bxcx pop word [bp + ?offset] pop word [bp + ?segment] pop cx pop bx .ret: lleave lret ; INP: word [ss:sp + 2] = segment ; word [ss:sp] = offset ; bx:cx = add/sub displacement ; OUT: CY if the displacement carries ; NC if not normalise_pointer_with_displacement_bxcx: %endif lframe near lpar word, segment lpar word, offset lpar_return lenter %ifn NEED_NORMALISE_POINTER_WITH_DISPLACEMENT cmp word [bp + ?offset], 7FFFh jbe .ret %endif push ax push cx push dx push word [bp + ?segment] push word [bp + ?offset] call pointer_to_linear %if NEED_NORMALISE_POINTER_WITH_DISPLACEMENT ; push bx ; ; sign-extend cx into bx:cx ; cmp cx, 8000h ; CY if < 8000h (NC if negative) ; cmc ; NC if positive ; sbb bx, bx ; 0 if was NC, -1 if was CY add cx, ax adc dx, bx ; dx:cx = dx:ax + bx:cx ; pop bx lahf ; ah = flags %else xchg ax, cx %endif %if 0 ; Adds in HMA support for this function. Not currently used. cmp dx, 10h ; dx:ax >= 10_0000h ? jb @F ; no, linear-to-pointer normally --> ; ja .error add cx, 10h ; jc .error mov word [bp + ?offset], cx or word [bp + ?segment], -1 jmp .return @@: %endif push bx mov bx, cx and cx, 15 mov word [bp + ?offset], cx mov cl, 4 @@: shr dx, 1 rcr bx, 1 loop @B mov word [bp + ?segment], bx pop bx ; test dx, dx ; jnz .error .return: %if NEED_NORMALISE_POINTER_WITH_DISPLACEMENT sahf ; restore flags from ah %endif pop dx pop cx pop ax %ifn NEED_NORMALISE_POINTER_WITH_DISPLACEMENT .ret: %endif lleave lret ; INP: word [ss:sp + 2] = segment ; word [ss:sp] = offset ; OUT: dx:ax = linear address pointer_to_linear: lframe near lpar word, segment lpar word, offset lenter mov ax, word [bp + ?segment] xor dx, dx push cx mov cx, 4 @@: shl ax, 1 rcl dx, 1 loop @B add ax, word [bp + ?offset] adc dx, cx ; cx = 0 here pop cx lleave lret align 16 init1_end: %assign num (init1_end - init1_start) %if _BRIEFLZ %define which iniblz %elif _LZ4 %define which inilz4 %elif _SNAPPY %define which inisz %elif _EXODECR %define which iniexo %elif _X %define which inix %elif _HEATSHRINK %define which inihs %elif _LZD %define which inilz %elif _LZO %define which inilzo %elif _LZSA2 %define which inilzsa2 %elif _APL %define which iniapl %elif _BZP %define which inibzp %endif %warning which: num bytes used for depacker