forked from KolibriOS/kolibrios
755c9fe4d1
git-svn-id: svn://kolibrios.org@6798 a494cfbc-eb01-0410-851d-a64ba20cac60
756 lines
20 KiB
PHP
756 lines
20 KiB
PHP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2004-2016. All rights reserved. ;;
|
|
;; Distributed under terms of the GNU General Public License. ;;
|
|
;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
$Revision$
|
|
|
|
; CD external functions
|
|
; in:
|
|
; esi -> path string in UTF-8
|
|
; ebx -> offset in file (qword)
|
|
; ecx = bytes to read
|
|
; edx -> buffer
|
|
; out:
|
|
; eax, ebx = return values for sysfunc 70
|
|
iglobal
|
|
align 4
|
|
fs_CdServices:
|
|
dd fs_CdRead
|
|
dd fs_CdReadFolder
|
|
dd fs_NotImplemented
|
|
dd fs_NotImplemented
|
|
dd fs_NotImplemented
|
|
dd fs_CdGetFileInfo
|
|
dd fs_NotImplemented
|
|
dd 0
|
|
dd fs_NotImplemented
|
|
dd fs_NotImplemented
|
|
fs_NumCdServices = ($ - fs_CdServices)/4
|
|
endg
|
|
|
|
uglobal
|
|
align 4
|
|
cd_current_pointer_of_input dd 0
|
|
cd_current_pointer_of_input_2 dd 0
|
|
cd_mem_location dd 0
|
|
cd_counter_block dd 0
|
|
cd_status dd 0
|
|
endg
|
|
|
|
;-----------------------------------------------------------------------------
|
|
fs_NotImplemented:
|
|
movi eax, ERROR_UNSUPPORTED_FS
|
|
ret
|
|
;-----------------------------------------------------------------------------
|
|
reserve_cd:
|
|
cli
|
|
cmp [cd_status], 0
|
|
je reserve_ok2
|
|
|
|
sti
|
|
call change_task
|
|
jmp reserve_cd
|
|
;-----------------------------------------------------------------------------
|
|
reserve_ok2:
|
|
push eax
|
|
mov eax, [CURRENT_TASK]
|
|
shl eax, 5
|
|
mov eax, [eax+CURRENT_TASK+TASKDATA.pid]
|
|
mov [cd_status], eax
|
|
pop eax
|
|
sti
|
|
ret
|
|
;-----------------------------------------------------------------------------
|
|
reserve_cd_channel:
|
|
pushad
|
|
mov eax, [cdpos]
|
|
dec eax
|
|
shr eax, 2
|
|
|
|
test eax, eax
|
|
jnz .1
|
|
|
|
cmp [ChannelNumber], 1
|
|
jne @f
|
|
|
|
mov ecx, ide_channel1_mutex
|
|
jmp .mutex_lock
|
|
;--------------------------------------
|
|
@@:
|
|
mov ecx, ide_channel2_mutex
|
|
jmp .mutex_lock
|
|
;--------------------------------------
|
|
.1:
|
|
dec eax
|
|
jnz .2
|
|
|
|
cmp [ChannelNumber], 1
|
|
jne @f
|
|
|
|
mov ecx, ide_channel3_mutex
|
|
jmp .mutex_lock
|
|
;--------------------------------------
|
|
@@:
|
|
mov ecx, ide_channel4_mutex
|
|
jmp .mutex_lock
|
|
;--------------------------------------
|
|
.2:
|
|
cmp [ChannelNumber], 1
|
|
jne @f
|
|
|
|
mov ecx, ide_channel5_mutex
|
|
jmp .mutex_lock
|
|
;--------------------------------------
|
|
@@:
|
|
mov ecx, ide_channel6_mutex
|
|
.mutex_lock:
|
|
call mutex_lock
|
|
popad
|
|
ret
|
|
;-----------------------------------------------------------------------------
|
|
free_cd_channel:
|
|
pushad
|
|
mov eax, [cdpos]
|
|
dec eax
|
|
shr eax, 2
|
|
|
|
test eax, eax
|
|
jnz .1
|
|
|
|
cmp [ChannelNumber], 1
|
|
jne @f
|
|
|
|
mov ecx, ide_channel1_mutex
|
|
jmp .mutex_unlock
|
|
;--------------------------------------
|
|
@@:
|
|
mov ecx, ide_channel2_mutex
|
|
jmp .mutex_unlock
|
|
;--------------------------------------
|
|
.1:
|
|
dec eax
|
|
jnz .2
|
|
|
|
cmp [ChannelNumber], 1
|
|
jne @f
|
|
|
|
mov ecx, ide_channel3_mutex
|
|
jmp .mutex_unlock
|
|
;--------------------------------------
|
|
@@:
|
|
mov ecx, ide_channel4_mutex
|
|
jmp .mutex_unlock
|
|
;--------------------------------------
|
|
.2:
|
|
cmp [ChannelNumber], 1
|
|
jne @f
|
|
|
|
mov ecx, ide_channel5_mutex
|
|
jmp .mutex_unlock
|
|
;--------------------------------------
|
|
@@:
|
|
mov ecx, ide_channel6_mutex
|
|
.mutex_unlock:
|
|
call mutex_unlock
|
|
popad
|
|
ret
|
|
|
|
;-----------------------------------------------------------------------------
|
|
fs_CdRead:
|
|
push edi
|
|
cmp byte [esi], 0
|
|
jnz @f
|
|
;--------------------------------------
|
|
.noaccess:
|
|
pop edi
|
|
;--------------------------------------
|
|
.noaccess_2:
|
|
or ebx, -1
|
|
mov eax, ERROR_ACCESS_DENIED
|
|
ret
|
|
;--------------------------------------
|
|
.noaccess_3:
|
|
pop eax edx ecx edi
|
|
jmp .noaccess_2
|
|
;--------------------------------------
|
|
@@:
|
|
call cd_find_lfn
|
|
jnc .found
|
|
|
|
pop edi
|
|
cmp [DevErrorCode], 0
|
|
jne .noaccess_2
|
|
|
|
or ebx, -1
|
|
mov eax, ERROR_FILE_NOT_FOUND
|
|
ret
|
|
;--------------------------------------
|
|
.found:
|
|
mov edi, [cd_current_pointer_of_input]
|
|
test byte [edi+25], 10b; do not allow read directories
|
|
jnz .noaccess
|
|
|
|
test ebx, ebx
|
|
jz .l1
|
|
|
|
cmp dword [ebx+4], 0
|
|
jz @f
|
|
|
|
xor ebx, ebx
|
|
;--------------------------------------
|
|
.reteof:
|
|
mov eax, 6; end of file
|
|
pop edi
|
|
ret
|
|
;--------------------------------------
|
|
@@:
|
|
mov ebx, [ebx]
|
|
;--------------------------------------
|
|
.l1:
|
|
push ecx edx
|
|
push 0
|
|
mov eax, [edi+10] ; real size of the file section
|
|
sub eax, ebx
|
|
jb .eof
|
|
|
|
cmp eax, ecx
|
|
jae @f
|
|
|
|
mov ecx, eax
|
|
mov byte [esp], 6
|
|
;--------------------------------------
|
|
@@:
|
|
mov eax, [edi+2]
|
|
mov [CDSectorAddress], eax
|
|
;--------------------------------------
|
|
; now eax=cluster, ebx=position, ecx=count, edx=buffer for data
|
|
.new_sector:
|
|
test ecx, ecx
|
|
jz .done
|
|
|
|
sub ebx, 2048
|
|
jae .next
|
|
|
|
add ebx, 2048
|
|
jnz .incomplete_sector
|
|
|
|
cmp ecx, 2048
|
|
jb .incomplete_sector
|
|
; we may read and memmove complete sector
|
|
mov [CDDataBuf_pointer], edx
|
|
call ReadCDWRetr ; read sector of file
|
|
cmp [DevErrorCode], 0
|
|
jne .noaccess_3
|
|
|
|
add edx, 2048
|
|
sub ecx, 2048
|
|
;--------------------------------------
|
|
.next:
|
|
inc dword [CDSectorAddress]
|
|
jmp .new_sector
|
|
;--------------------------------------
|
|
.incomplete_sector:
|
|
; we must read and memmove incomplete sector
|
|
mov [CDDataBuf_pointer], CDDataBuf
|
|
call ReadCDWRetr ; read sector of file
|
|
cmp [DevErrorCode], 0
|
|
jne .noaccess_3
|
|
|
|
push ecx
|
|
add ecx, ebx
|
|
cmp ecx, 2048
|
|
jbe @f
|
|
|
|
mov ecx, 2048
|
|
;--------------------------------------
|
|
@@:
|
|
sub ecx, ebx
|
|
push edi esi ecx
|
|
mov edi, edx
|
|
lea esi, [CDDataBuf + ebx]
|
|
cld
|
|
rep movsb
|
|
pop ecx esi edi
|
|
add edx, ecx
|
|
sub [esp], ecx
|
|
pop ecx
|
|
xor ebx, ebx
|
|
jmp .next
|
|
;--------------------------------------
|
|
.done:
|
|
mov ebx, edx
|
|
pop eax edx ecx edi
|
|
sub ebx, edx
|
|
ret
|
|
;--------------------------------------
|
|
.eof:
|
|
mov ebx, edx
|
|
pop eax edx ecx
|
|
sub ebx, edx
|
|
jmp .reteof
|
|
|
|
;-----------------------------------------------------------------------------
|
|
fs_CdReadFolder:
|
|
push edi
|
|
call cd_find_lfn
|
|
jnc .found
|
|
pop edi
|
|
cmp [DevErrorCode], 0
|
|
jne .noaccess_1
|
|
or ebx, -1
|
|
mov eax, ERROR_FILE_NOT_FOUND
|
|
ret
|
|
|
|
.found:
|
|
mov edi, [cd_current_pointer_of_input]
|
|
test byte [edi+25], 10b ; do not allow read directories
|
|
jnz .found_dir
|
|
pop edi
|
|
.noaccess_1:
|
|
or ebx, -1
|
|
mov eax, ERROR_ACCESS_DENIED
|
|
ret
|
|
|
|
.end_buffer:
|
|
pop edx eax
|
|
sub eax, 2048 ; directory is over?
|
|
ja .read_to_buffer
|
|
mov eax, [cd_counter_block]
|
|
mov [edx+8], eax
|
|
mov eax, [ebx]
|
|
sub [edx+4], eax
|
|
xor eax, eax
|
|
dec ecx
|
|
js @f
|
|
mov al, ERROR_END_OF_FILE
|
|
@@:
|
|
pop ecx edi
|
|
mov ebx, [edx+4]
|
|
ret
|
|
|
|
.found_dir:
|
|
mov eax, [edi+2] ; eax=cluster
|
|
mov [CDSectorAddress], eax
|
|
mov eax, [edi+10] ; directory size
|
|
push eax ecx
|
|
mov edi, edx
|
|
mov ecx, 32/4
|
|
xor eax, eax
|
|
rep stosd
|
|
pop ecx eax
|
|
mov byte [edx], 1 ; version
|
|
mov [cd_mem_location], edx
|
|
add [cd_mem_location], 32
|
|
mov [cd_counter_block], dword 0
|
|
dec dword [CDSectorAddress]
|
|
push ecx
|
|
.read_to_buffer:
|
|
inc dword [CDSectorAddress]
|
|
mov [CDDataBuf_pointer], CDDataBuf
|
|
call ReadCDWRetr ; read sector of directory
|
|
cmp [DevErrorCode], 0
|
|
jne .noaccess_1
|
|
mov [cd_current_pointer_of_input_2], CDDataBuf
|
|
push eax edx
|
|
.get_names_from_buffer:
|
|
call cd_get_name
|
|
jc .end_buffer
|
|
inc dword [cd_counter_block]
|
|
mov eax, [cd_counter_block]
|
|
cmp [ebx], eax
|
|
jae .get_names_from_buffer
|
|
test ecx, ecx
|
|
jz .get_names_from_buffer
|
|
mov edi, [cd_counter_block]
|
|
mov [edx+4], edi
|
|
dec ecx
|
|
mov esi, ebp
|
|
call cd_get_parameters_of_file
|
|
add edi, 40
|
|
mov ax, '.'
|
|
cmp dword[ebx+4], 2
|
|
jz .utf16
|
|
cmp dword[ebx+4], 3
|
|
jz .utf8
|
|
cmp [cd_counter_block], 2
|
|
jbe .parentDirectory
|
|
@@:
|
|
lodsw
|
|
xchg ah, al
|
|
call uni2ansi_char
|
|
stosb
|
|
call .checkForEnd
|
|
jc @b
|
|
@@:
|
|
mov [edi], byte 0
|
|
add [cd_mem_location], 304
|
|
jmp .get_names_from_buffer
|
|
|
|
.parentDirectory:
|
|
stosb
|
|
cmp [cd_counter_block], 2
|
|
jnz @b
|
|
stosb
|
|
jmp @b
|
|
|
|
.utf8:
|
|
add [cd_mem_location], 256
|
|
cmp [cd_counter_block], 2
|
|
jbe .parentDirectory
|
|
push ecx
|
|
mov ecx, 519
|
|
@@:
|
|
lodsw
|
|
xchg ah, al
|
|
call UTF16to8
|
|
js @f
|
|
call .checkForEnd
|
|
jc @b
|
|
@@:
|
|
pop ecx
|
|
mov [edi], byte 0
|
|
add [cd_mem_location], 304
|
|
jmp .get_names_from_buffer
|
|
|
|
.checkForEnd:
|
|
mov ax, [esi]
|
|
cmp ax, 3B00h ; ';'
|
|
jz @f
|
|
; check for files not ending with separator
|
|
movzx eax, byte [ebp-33]
|
|
add eax, ebp
|
|
sub eax, 34
|
|
cmp esi, eax
|
|
jz @f
|
|
; check the end of the directory
|
|
movzx eax, byte [ebp-1]
|
|
add eax, ebp
|
|
cmp esi, eax
|
|
@@:
|
|
ret
|
|
|
|
.utf16:
|
|
cmp [cd_counter_block], 2
|
|
jbe .utf16ParentDirectory
|
|
@@:
|
|
lodsw
|
|
xchg ah, al
|
|
stosw
|
|
call .checkForEnd
|
|
jc @b
|
|
@@:
|
|
mov [edi], word 0
|
|
add [cd_mem_location], 560
|
|
jmp .get_names_from_buffer
|
|
|
|
.utf16ParentDirectory:
|
|
stosw
|
|
cmp [cd_counter_block], 2
|
|
jnz @b
|
|
stosw
|
|
jmp @b
|
|
|
|
cd_get_parameters_of_file:
|
|
mov edi, [cd_mem_location]
|
|
cd_get_parameters_of_file_1:
|
|
; get file attributes
|
|
xor eax, eax
|
|
; file is not archived
|
|
inc eax
|
|
shl eax, 1
|
|
; is a directory?
|
|
test [ebp-8], byte 2
|
|
jz .file
|
|
inc eax
|
|
.file:
|
|
; not as a volume label in the FAT, in this form not available
|
|
; file is not a system
|
|
shl eax, 3
|
|
; file is hidden? (attribute of existence)
|
|
test [ebp-8], byte 1
|
|
jz .hidden
|
|
inc eax
|
|
.hidden:
|
|
shl eax, 1
|
|
; file is always read-only, as this CD
|
|
inc eax
|
|
mov [edi], eax
|
|
mov eax, [ebx+4]
|
|
mov [edi+4], eax
|
|
; get the time to file
|
|
; hour
|
|
movzx eax, byte [ebp-12]
|
|
shl eax, 8
|
|
; minute
|
|
mov al, [ebp-11]
|
|
shl eax, 8
|
|
; second
|
|
mov al, [ebp-10]
|
|
; file creation time
|
|
mov [edi+8], eax
|
|
; last access time
|
|
mov [edi+16], eax
|
|
; last write time
|
|
mov [edi+24], eax
|
|
; get date for file
|
|
; year
|
|
movzx eax, byte [ebp-15]
|
|
add eax, 1900
|
|
shl eax, 8
|
|
; month
|
|
mov al, [ebp-14]
|
|
shl eax, 8
|
|
; day
|
|
mov al, [ebp-13]
|
|
; file creation date
|
|
mov [edi+12], eax
|
|
; last access date
|
|
mov [edi+20], eax
|
|
; last write date
|
|
mov [edi+28], eax
|
|
; get the file size in bytes
|
|
xor eax, eax
|
|
mov [edi+32+4], eax
|
|
mov eax, [ebp-23]
|
|
mov [edi+32], eax
|
|
ret
|
|
|
|
;-----------------------------------------------------------------------------
|
|
fs_CdGetFileInfo:
|
|
cmp byte [esi], 0
|
|
jnz @f
|
|
|
|
mov eax, 2
|
|
ret
|
|
;--------------------------------------
|
|
@@:
|
|
push edi
|
|
call cd_find_lfn
|
|
pushfd
|
|
cmp [DevErrorCode], 0
|
|
jz @f
|
|
|
|
popfd
|
|
pop edi
|
|
mov eax, 11
|
|
ret
|
|
;--------------------------------------
|
|
@@:
|
|
popfd
|
|
jnc @f
|
|
|
|
pop edi
|
|
mov eax, ERROR_FILE_NOT_FOUND
|
|
ret
|
|
;--------------------------------------
|
|
@@:
|
|
|
|
mov edi, edx
|
|
push ebp
|
|
mov ebp, [cd_current_pointer_of_input]
|
|
add ebp, 33
|
|
call cd_get_parameters_of_file_1
|
|
pop ebp
|
|
and dword [edi+4], 0
|
|
pop edi
|
|
xor eax, eax
|
|
ret
|
|
;-----------------------------------------------------------------------------
|
|
cd_find_lfn:
|
|
mov [cd_appl_data], 0
|
|
; in: esi -> path string in UTF-8
|
|
; out: [cd_current_pointer_of_input] -> direntry, CF=1 -> file not found
|
|
push eax esi
|
|
; Sector 16 - start set of volume descriptors
|
|
call WaitUnitReady
|
|
cmp [DevErrorCode], 0
|
|
jne .access_denied
|
|
|
|
call prevent_medium_removal
|
|
; testing of reading
|
|
mov [CDSectorAddress], dword 16
|
|
mov [CDDataBuf_pointer], CDDataBuf
|
|
call ReadCDWRetr;_1
|
|
cmp [DevErrorCode], 0
|
|
jne .access_denied
|
|
|
|
; calculation of the last session
|
|
call WaitUnitReady
|
|
cmp [DevErrorCode], 0
|
|
jne .access_denied
|
|
|
|
call Read_TOC
|
|
mov ah, [CDDataBuf+4+4]
|
|
mov al, [CDDataBuf+4+5]
|
|
shl eax, 16
|
|
mov ah, [CDDataBuf+4+6]
|
|
mov al, [CDDataBuf+4+7]
|
|
add eax, 15
|
|
mov [CDSectorAddress], eax
|
|
; mov [CDSectorAddress],dword 15
|
|
mov [CDDataBuf_pointer], CDDataBuf
|
|
;--------------------------------------
|
|
.start:
|
|
inc dword [CDSectorAddress]
|
|
call ReadCDWRetr;_1
|
|
cmp [DevErrorCode], 0
|
|
jne .access_denied
|
|
|
|
.start_check:
|
|
; checking for "lice"
|
|
cmp [CDDataBuf+1], dword 'CD00'
|
|
jne .access_denied
|
|
|
|
cmp [CDDataBuf+5], byte '1'
|
|
jne .access_denied
|
|
; sector is the terminator of set of descriptors volumes?
|
|
cmp [CDDataBuf], byte 0xff
|
|
je .access_denied
|
|
; sector is an additional and improved descriptor of volume?
|
|
cmp [CDDataBuf], byte 0x2
|
|
jne .start
|
|
; sector is an additional descriptor of volume?
|
|
cmp [CDDataBuf+6], byte 0x1
|
|
jne .start
|
|
|
|
; parameters of root directory
|
|
mov eax, [CDDataBuf+0x9c+2]; start of root directory
|
|
mov [CDSectorAddress], eax
|
|
mov eax, [CDDataBuf+0x9c+10]; size of root directory
|
|
cmp byte [esi], 0
|
|
jnz @f
|
|
|
|
mov [cd_current_pointer_of_input], CDDataBuf+0x9c
|
|
jmp .done
|
|
;--------------------------------------
|
|
@@:
|
|
; start the search
|
|
.mainloop:
|
|
dec dword [CDSectorAddress]
|
|
;--------------------------------------
|
|
.read_to_buffer:
|
|
inc dword [CDSectorAddress]
|
|
mov [CDDataBuf_pointer], CDDataBuf
|
|
call ReadCDWRetr ; read sector of directory
|
|
cmp [DevErrorCode], 0
|
|
jne .access_denied
|
|
call cd_find_name_in_buffer
|
|
jnc .found
|
|
sub eax, 2048
|
|
; directory is over?
|
|
cmp eax, 0
|
|
ja .read_to_buffer
|
|
; desired element of chain is not found
|
|
.access_denied:
|
|
pop esi eax
|
|
mov [cd_appl_data], 1
|
|
stc
|
|
ret
|
|
;--------------------------------------
|
|
; desired element of chain found
|
|
.found:
|
|
; the end of the file path
|
|
cmp byte [esi-1], 0
|
|
jz .done
|
|
mov eax, [cd_current_pointer_of_input]
|
|
push dword [eax+2]
|
|
pop dword [CDSectorAddress] ; beginning of the directory
|
|
mov eax, [eax+2+8] ; size of directory
|
|
jmp .mainloop
|
|
;--------------------------------------
|
|
; file pointer found
|
|
.done:
|
|
pop esi eax
|
|
mov [cd_appl_data], 1
|
|
clc
|
|
ret
|
|
;-----------------------------------------------------------------------------
|
|
cd_find_name_in_buffer:
|
|
mov [cd_current_pointer_of_input_2], CDDataBuf
|
|
;--------------------------------------
|
|
.start:
|
|
call cd_get_name
|
|
jc .not_found
|
|
|
|
call cd_compare_name
|
|
jc .start
|
|
;--------------------------------------
|
|
.found:
|
|
clc
|
|
ret
|
|
;--------------------------------------
|
|
.not_found:
|
|
stc
|
|
ret
|
|
;-----------------------------------------------------------------------------
|
|
cd_get_name:
|
|
push eax
|
|
mov ebp, [cd_current_pointer_of_input_2]
|
|
mov [cd_current_pointer_of_input], ebp
|
|
mov eax, [ebp]
|
|
test eax, eax ; entry's is over?
|
|
jz .next_sector
|
|
|
|
cmp ebp, CDDataBuf+2048 ; buffer is over?
|
|
jae .next_sector
|
|
|
|
movzx eax, byte [ebp]
|
|
add [cd_current_pointer_of_input_2], eax ; next entry of directory
|
|
add ebp, 33; pointer is set to the beginning of the name
|
|
pop eax
|
|
clc
|
|
ret
|
|
;--------------------------------------
|
|
.next_sector:
|
|
pop eax
|
|
stc
|
|
ret
|
|
;-----------------------------------------------------------------------------
|
|
cd_compare_name:
|
|
; in: esi -> UTF-8 name, ebp -> UTF-16BE name
|
|
; out: CF=0 -> names match, esi -> next component of name
|
|
; CF=1 -> esi is not changed
|
|
push edx edi eax esi
|
|
mov edi, ebp
|
|
.loop:
|
|
call utf8to16
|
|
call utf16toUpper
|
|
mov edx, eax
|
|
mov ax, [edi]
|
|
xchg al, ah
|
|
call utf16toUpper
|
|
cmp ax, dx
|
|
jne .name_not_coincide
|
|
add edi, 2
|
|
cmp [esi], byte '/' ; path separator is end of current element
|
|
je .done
|
|
cmp [esi], byte 0 ; path separator end of name
|
|
jne .loop
|
|
.done:
|
|
; check end of file
|
|
cmp [edi], word 3B00h; separator end of file ';'
|
|
je .done_1
|
|
; check for files not ending with separator
|
|
movzx eax, byte [ebp-33]
|
|
add eax, ebp
|
|
sub eax, 34
|
|
cmp edi, eax
|
|
je .done_1
|
|
; check the end of directory
|
|
movzx eax, byte [ebp-1]
|
|
add eax, ebp
|
|
cmp edi, eax
|
|
jne .name_not_coincide
|
|
.done_1:
|
|
pop eax eax edi edx
|
|
inc esi
|
|
ret
|
|
|
|
.name_not_coincide:
|
|
pop esi eax edi edx
|
|
stc
|
|
ret
|