Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 4286 → Rev 4287

/kernel/branches/Kolibri-acpi/skin/left_1.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Deleted: svn:mime-type
-application/octet-stream
\ No newline at end of property
/kernel/branches/Kolibri-acpi/skin/left.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Deleted: svn:mime-type
-application/octet-stream
\ No newline at end of property
/kernel/branches/Kolibri-acpi/skin/base_1.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Deleted: svn:mime-type
-application/octet-stream
\ No newline at end of property
/kernel/branches/Kolibri-acpi/skin/base.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Deleted: svn:mime-type
-application/octet-stream
\ No newline at end of property
/kernel/branches/Kolibri-acpi/skin/me_skin.inc
File deleted
Property changes:
Deleted: svn:keywords
-Rev
\ No newline at end of property
/kernel/branches/Kolibri-acpi/skin/oper_1.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Deleted: svn:mime-type
-application/octet-stream
\ No newline at end of property
/kernel/branches/Kolibri-acpi/skin/oper.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Deleted: svn:mime-type
-application/octet-stream
\ No newline at end of property
/kernel/branches/Kolibri-acpi/skin/default.asm
File deleted
Property changes:
Deleted: svn:keywords
-Rev
\ No newline at end of property
/kernel/branches/Kolibri-acpi/skin/myblue.dtp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Deleted: svn:mime-type
-application/octet-stream
\ No newline at end of property
/kernel/branches/Kolibri-acpi/skin
Property changes:
Deleted: svn:ignore
-*.mnt
-lang.inc
-*.bat
-out.txt
-scin*
-*.obj
/kernel/branches/Kolibri-acpi/blkdev/disk.inc
1213,8 → 1213,40
; ecx = partition number, esi+ebp = ASCIIZ name
fs_dyndisk:
dec ecx ; convert to zero-based partition index
pop edx edx edx eax ; edx = pointer to DISK, eax = NULL or edx
pop edx edx edx ; edx = pointer to DISK, dword [esp] = NULL or edx
; If the driver does not support insert notifications and we are the only fs
; operation with this disk, ask the driver whether the media
; was inserted/removed/changed. Otherwise, assume that media status is valid.
test byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION
jz .media_accurate
push ecx esi
mov esi, edx
cmp dword [esp+8], 0
jz .test_no_media
cmp [esi+DISK.MediaRefCount], 2
jnz .media_accurate_pop
lea edx, [esi+DISK.MediaInfo]
and [edx+DISKMEDIAINFO.Flags], 0
mov al, DISKFUNC.querymedia
stdcall disk_call_driver, edx
test eax, eax
jz .media_accurate_pop
stdcall disk_media_dereference ; drop our reference so that disk_media_changed could close the media
stdcall disk_media_changed, esi, 0
and dword [esp+8], 0 ; no media
.test_no_media:
stdcall disk_media_changed, esi, 1 ; issue fake notification
; if querymedia() inside disk_media_changed returns error, the notification is ignored
cmp [esi+DISK.MediaInserted], 0
jz .media_accurate_pop
lock inc [esi+DISK.MediaRefCount]
mov dword [esp+8], esi
.media_accurate_pop:
mov edx, esi
pop esi ecx
.media_accurate:
pop eax
test eax, eax
jz .nomedia
.main:
cmp ecx, [edx+DISK.NumPartitions]
1252,30 → 1284,6
.nomedia:
test ecx, ecx
jnz .notfound
test byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION
jz .deverror
; if the driver does not support insert notifications and we are the only fs
; operation with this disk, issue the fake insert notification; if media is
; still not inserted, 'disk_media_changed' will detect this and do nothing
lea ecx, [edx+DISK.MediaLock]
call mutex_lock
cmp [edx+DISK.MediaRefCount], 1
jnz .noluck
call mutex_unlock
push edx
stdcall disk_media_changed, edx, 1
pop edx
lea ecx, [edx+DISK.MediaLock]
call mutex_lock
cmp [edx+DISK.MediaInserted], 0
jz .noluck
lock inc [edx+DISK.MediaRefCount]
call mutex_unlock
xor ecx, ecx
jmp .main
.noluck:
call mutex_unlock
.deverror:
mov dword [esp+32], ERROR_DEVICE
mov esi, edx
call disk_dereference
/kernel/branches/Kolibri-acpi/blkdev/fdc.inc
21,40 → 21,48
ret
 
save_image:
call reserve_flp
call restorefatchain
cmp [ramdisk_actual_size], FLOPPY_CAPACITY
jnz .fail
pusha
call check_label
mov ecx, floppy_mutex
call mutex_lock
mov [flp_number], bl
call floppy_read_bootsector
cmp [FDC_Status], 0
jne unnecessary_save_image
jne .unnecessary_save_image
mov [FDD_Track], 0; Цилиндр
mov [FDD_Head], 0; Сторона
mov [FDD_Sector], 1; Сектор
mov esi, RAMDISK
call SeekTrack
save_image_1:
push esi
.save_image_1:
call take_data_from_application_1
pop esi
add esi, 512
call WriteSectWithRetr
; call WriteSector
cmp [FDC_Status], 0
jne unnecessary_save_image
jne .unnecessary_save_image
inc [FDD_Sector]
cmp [FDD_Sector], 19
jne save_image_1
jne .save_image_1
mov [FDD_Sector], 1
inc [FDD_Head]
cmp [FDD_Head], 2
jne save_image_1
jne .save_image_1
mov [FDD_Head], 0
inc [FDD_Track]
call SeekTrack
cmp [FDD_Track], 80
jne save_image_1
unnecessary_save_image:
jne .save_image_1
.unnecessary_save_image:
cmp [FDC_Status], 0
pushf
mov ecx, floppy_mutex
call mutex_unlock
popf
popa
mov [flp_status], 0
jnz .fail
xor eax, eax
ret
 
.fail:
movi eax, 1
ret
/kernel/branches/Kolibri-acpi/blkdev/flp_drv.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20,8 → 20,7
; add edi,ecx
give_back_application_data_1:
mov esi, FDD_BUFF;FDD_DataBuffer ;0x40000
xor ecx, ecx
mov cx, 128
mov ecx, 128
cld
rep movsd
ret
32,8 → 31,7
; add esi,ecx
take_data_from_application_1:
mov edi, FDD_BUFF;FDD_DataBuffer ;0x40000
xor ecx, ecx
mov cx, 128
mov ecx, 128
cld
rep movsd
ret
122,6 → 120,7
;* AL - выводимый байт. *
;***********************************
FDCDataOutput:
; DEBUGF 1,'K : FDCDataOutput(%x)',al
; pusha
push eax ecx edx
mov AH, AL ;запомнить байт в AH
137,6 → 136,7
je @@OutByteToFDC
loop @@TestRS
; Ошибка тайм-аута
; DEBUGF 1,' timeout\n'
mov [FDC_Status], FDC_TimeOut
jmp @@End_5
; Вывести байт в порт данных
144,6 → 144,7
inc DX
mov AL, AH
out DX, AL
; DEBUGF 1,' ok\n'
@@End_5:
; popa
pop edx ecx eax
170,6 → 171,7
je @@GetByteFromFDC
loop @@TestRS_1
; Ошибка тайм-аута
; DEBUGF 1,'K : FDCDataInput: timeout\n'
mov [FDC_Status], FDC_TimeOut
jmp @@End_6
; Ввести байт из порта данных
176,6 → 178,7
@@GetByteFromFDC:
inc DX
in AL, DX
; DEBUGF 1,'K : FDCDataInput: %x\n',al
@@End_6:
pop DX
pop ECX
185,6 → 188,7
;* ОБРАБОТЧИК ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД *
;*********************************************
FDCInterrupt:
; dbgstr 'FDCInterrupt'
; Установить флаг прерывания
mov [FDD_IntFlag], 1
mov al, 1
207,12 → 211,12
jnz @@End_7 ;прерывание произошло
mov eax, [timer_ticks]
sub eax, [TickCounter]
cmp eax, 50 ;25 ;5 ;ожидать 5 тиков
cmp eax, 200;50 ;25 ;5 ;ожидать 5 тиков
jb @@TestRS_2
; jl @@TestRS_2
; Ошибка тайм-аута
; dbgstr 'WaitFDCInterrupt: timeout'
mov [FDC_Status], FDC_TimeOut
; mov [flp_status],0
@@End_7:
popa
ret
221,6 → 225,7
;* ВКЛЮЧИТЬ МОТОР ДИСКОВОДА "A:" *
;*********************************
FDDMotorON:
; dbgstr 'FDDMotorON'
pusha
; cmp [fdd_motor_status],1
; je fdd_motor_on
252,6 → 257,20
sub eax, [TickCounter]
cmp eax, 50 ;10
jb @@dT
; Read results of RESET command
push 4
; DEBUGF 1,'K : floppy reset results:'
@@:
mov al, 8
call FDCDataOutput
call FDCDataInput
; DEBUGF 1,' %x',al
call FDCDataInput
; DEBUGF 1,' %x',al
dec dword [esp]
jnz @b
; DEBUGF 1,'\n'
pop eax
cmp [flp_number], 1
jne fdd_motor_on_B
mov [fdd_motor_status], 1
275,8 → 294,6
;* ПРОВЕРКА ЗАДЕРЖКИ ВЫКЛЮЧЕНИЯ МОТОРА *
;*****************************************
proc check_fdd_motor_status_has_work?
cmp [flp_status], 0
jnz .yes
cmp [fdd_motor_status], 0
jz .no
mov eax, [timer_ticks]
303,7 → 320,6
call FDDMotorOFF
mov [fdd_motor_status], 0
end_check_fdd_motor_status_1:
mov [flp_status], 0
end_check_fdd_motor_status:
ret
 
311,6 → 327,7
;* ВЫКЛЮЧИТЬ МОТОР ДИСКОВОДА *
;**********************************
FDDMotorOFF:
; dbgstr 'FDDMotorOFF'
push AX
push DX
cmp [flp_number], 1
323,8 → 340,8
pop DX
pop AX
; сброс флагов кеширования в связи с устареванием информации
mov [root_read], 0
mov [flp_fat], 0
or [floppy_media_flags+0], FLOPPY_MEDIA_NEED_RESCAN
or [floppy_media_flags+1], FLOPPY_MEDIA_NEED_RESCAN
ret
 
FDDMotorOFF_A:
343,8 → 360,11
;* РЕКАЛИБРОВКА ДИСКОВОДА "A:" *
;*******************************
RecalibrateFDD:
; dbgstr 'RecalibrateFDD'
pusha
call save_timer_fdd_motor
; Сбросить флаг прерывания
mov [FDD_IntFlag], 0
; Подать команду "Рекалибровка"
mov AL, 07h
call FDCDataOutput
352,10 → 372,18
call FDCDataOutput
; Ожидать завершения операции
call WaitFDCInterrupt
; cmp [FDC_Status],0
; je no_fdc_status_error
; mov [flp_status],0
;no_fdc_status_error:
cmp [FDC_Status], 0
jne .fail
; Read results of RECALIBRATE command
; DEBUGF 1,'K : floppy recalibrate results:'
mov al, 8
call FDCDataOutput
call FDCDataInput
; DEBUGF 1,' %x',al
call FDCDataInput
; DEBUGF 1,' %x',al
; DEBUGF 1,'\n'
.fail:
call save_timer_fdd_motor
popa
ret
368,6 → 396,7
;* Результат операции заносится в FDC_Status. *
;*****************************************************
SeekTrack:
; dbgstr 'SeekTrack'
pusha
call save_timer_fdd_motor
; Сбросить флаг прерывания
402,17 → 431,20
cmp AL, [FDD_Track]
jne @@Err
; Номер головки совпадает с заданным?
mov AL, [FDC_ST0]
and AL, 100b
shr AL, 2
cmp AL, [FDD_Head]
jne @@Err
; The H bit (Head Address) in ST0 will always return a "0" (c) 82077AA datasheet,
; description of SEEK command. So we can not verify the proper head.
; mov AL, [FDC_ST0]
; and AL, 100b
; shr AL, 2
; cmp AL, [FDD_Head]
; jne @@Err
; Операция завершена успешно
; dbgstr 'SeekTrack: FDC_Normal'
mov [FDC_Status], FDC_Normal
jmp @@Exit
@@Err: ; Трек не найден
; dbgstr 'SeekTrack: FDC_TrackNotFound'
mov [FDC_Status], FDC_TrackNotFound
; mov [flp_status],0
@@Exit:
call save_timer_fdd_motor
popa
429,6 → 461,7
;* содержимое сектора будет занесено в FDD_DataBuffer. *
;*******************************************************
ReadSector:
; dbgstr 'ReadSector'
pushad
call save_timer_fdd_motor
; Сбросить флаг прерывания
468,11 → 501,12
call GetStatusInfo
test [FDC_ST0], 11011000b
jnz @@Err_1
; dbgstr 'ReadSector: FDC_Normal'
mov [FDC_Status], FDC_Normal
jmp @@Exit_1
@@Err_1:
; dbgstr 'ReadSector: FDC_SectorNotFound'
mov [FDC_Status], FDC_SectorNotFound
; mov [flp_status],0
@@Exit_1:
call save_timer_fdd_motor
popad
511,12 → 545,10
inc [RecalRepCounter]
cmp [RecalRepCounter], 3
jb @@TryAgain
; mov [flp_status],0
@@Exit_2:
popa
ret
@@Err_3:
mov [flp_status], 0
popa
ret
 
531,6 → 563,7
;* содержимое FDD_DataBuffer будет занесено в сектор. *
;*******************************************************
WriteSector:
; dbgstr 'WriteSector'
pushad
call save_timer_fdd_motor
; Сбросить флаг прерывания
616,7 → 649,6
popa
ret
@@Err_4:
mov [flp_status], 0
popa
ret
 
642,3 → 674,276
pop AX
ret
 
; Interface for disk subsystem.
; Assume fixed capacity for 1.44M.
FLOPPY_CAPACITY = 2880 ; in sectors
 
iglobal
align 4
floppy_functions:
dd .size
dd 0 ; no close() function
dd 0 ; no closemedia() function
dd floppy_querymedia
dd floppy_read
dd floppy_write
dd 0 ; no flush() function
dd 0 ; no adjust_cache_size() function
.size = $ - floppy_functions
endg
 
uglobal
floppy_media_flags rb 2
n_sector dd 0 ; temporary save for sector value
flp_number db 0 ; 1- Floppy A, 2-Floppy B
old_track db 0 ; old value track
flp_label rb 15*2 ; Label and ID of inserted floppy disk
align 4
; Hardware does not allow to work with two floppies in parallel,
; so there is one mutex guarding access to any floppy.
floppy_mutex MUTEX
endg
; Meaning of bits in floppy_media_flags
FLOPPY_MEDIA_PRESENT = 1 ; media was present when last asked
FLOPPY_MEDIA_NEED_RESCAN = 2 ; media was possibly changed, need to rescan
FLOPPY_MEDIA_LABEL_CHANGED = 4 ; temporary state
 
iglobal
floppy1_name db 'fd',0
floppy2_name db 'fd2',0
endg
 
; This function is called in boot process.
; It creates filesystems /fd and/or /fd2, if the system has one/two floppy drives.
proc floppy_init
mov ecx, floppy_mutex
call mutex_init
; First floppy is present if [DRIVE_DATA] and 0xF0 is nonzero.
test byte [DRIVE_DATA], 0xF0
jz .no1
stdcall disk_add, floppy_functions, floppy1_name, 1, DISK_NO_INSERT_NOTIFICATION
.no1:
; Second floppy is present if [DRIVE_DATA] and 0x0F is nonzero.
test byte [DRIVE_DATA], 0x0F
jz .no2
stdcall disk_add, floppy_functions, floppy2_name, 2, DISK_NO_INSERT_NOTIFICATION
.no2:
ret
endp
 
; Returns information about disk media.
; Floppy drives do not support insert notifications,
; DISK_NO_INSERT_NOTIFICATION is set,
; the disk subsystem calls this function before each filesystem operation.
; If the media has changed, return error for the first call as signal
; to finalize work with old media and the true geometry for the second call.
; Assume that media is (possibly) changed anytime when motor is off.
proc floppy_querymedia
virtual at esp+4
.userdata dd ?
.info dd ?
end virtual
; 1. Acquire the global lock.
mov ecx, floppy_mutex
call mutex_lock
mov edx, [.userdata] ; 1 for /fd, 2 for /fd2
; 2. If the media was reported and has been changed, forget it and report an error.
mov al, [floppy_media_flags+edx-1]
and al, FLOPPY_MEDIA_PRESENT + FLOPPY_MEDIA_NEED_RESCAN
cmp al, FLOPPY_MEDIA_PRESENT + FLOPPY_MEDIA_NEED_RESCAN
jnz .not_reported
.no_media:
mov [floppy_media_flags+edx-1], 0
.return_no_media:
mov ecx, floppy_mutex
call mutex_unlock
mov eax, DISK_STATUS_NO_MEDIA
retn 8
.not_reported:
; 3. If we are in the temporary state LABEL_CHANGED, this is the second call
; after intermediate DISK_STATUS_NO_MEDIA due to media change;
; clear the flag and return the current geometry without rereading the bootsector.
cmp [floppy_media_flags+edx-1], FLOPPY_MEDIA_LABEL_CHANGED
jz .report_geometry
; 4. Try to read the bootsector.
mov [flp_number], dl
mov [FDC_Status], 0
call floppy_read_bootsector
; 5. If reading bootsector failed, assume that media is not present.
mov edx, [.userdata]
cmp [FDC_Status], 0
jnz .no_media
; 6. Check whether the previous status is "present". If not, go to 10.
push esi edi
imul edi, edx, 15
add edi, flp_label-15
mov esi, FDD_BUFF+39
mov ecx, 15
test [floppy_media_flags+edx-1], FLOPPY_MEDIA_PRESENT
jz .set_label
; 7. Compare the old label with the current one.
rep cmpsb
; 8. If the label has not changed, go to 11.
jz .ok
; 9. If the label has changed, store it, enter temporary state LABEL_CHANGED
; and report DISK_STATUS_NO_MEDIA.
; dbgstr 'floppy label changed'
add esi, ecx
add edi, ecx
mov ecx, 15
sub esi, ecx
sub edi, ecx
rep movsb
mov [floppy_media_flags+edx-1], FLOPPY_MEDIA_LABEL_CHANGED
pop edi esi
jmp .return_no_media
.set_label:
; 10. The previous state was "not present". Copy the label.
rep movsb
.ok:
pop edi esi
.report_geometry:
; 11. Fill DISKMEDIAINFO structure.
mov ecx, [.info]
and [ecx+DISKMEDIAINFO.Flags], 0
mov [ecx+DISKMEDIAINFO.SectorSize], 512
mov dword [ecx+DISKMEDIAINFO.Capacity], FLOPPY_CAPACITY
and dword [ecx+DISKMEDIAINFO.Capacity+4], 0
; 12. Update state: media is present, data are actual.
mov [floppy_media_flags+edx-1], FLOPPY_MEDIA_PRESENT
; 13. Release the global lock and return successful status.
mov ecx, floppy_mutex
call mutex_unlock
xor eax, eax
retn 8
endp
 
proc floppy_read_bootsector
pushad
mov [FDD_Track], 0; Цилиндр
mov [FDD_Head], 0; Сторона
mov [FDD_Sector], 1; Сектор
call FDDMotorON
call RecalibrateFDD
cmp [FDC_Status], 0
jne .nothing
call SeekTrack
cmp [FDC_Status], 0
jne .nothing
call ReadSectWithRetr
.nothing:
popad
ret
endp
 
read_chs_sector:
call calculate_chs
call ReadSectWithRetr
ret
 
save_chs_sector:
call calculate_chs
call WriteSectWithRetr
ret
 
calculate_chs:
mov bl, [FDD_Track]
mov [old_track], bl
mov ebx, 18
xor edx, edx
div ebx
inc edx
mov [FDD_Sector], dl
mov edx, eax
shr eax, 1
and edx, 1
mov [FDD_Track], al
mov [FDD_Head], dl
mov dl, [old_track]
cmp dl, [FDD_Track]
je no_seek_track_1
call SeekTrack
no_seek_track_1:
ret
 
; Writes one or more sectors to the device.
proc floppy_write
mov dl, 1
jmp floppy_read_write
endp
 
; Reads one or more sectors from the device.
proc floppy_read
mov dl, 0
endp
 
; Common part of floppy_read and floppy_write.
proc floppy_read_write userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword
virtual at ebp-8
.sectors_todo dd ?
.operation db ?
end virtual
push edx ; save operation code to [.operation]
; 1. Get number of sectors to read/write
; and zero number of sectors that were actually read/written.
mov eax, [numsectors_ptr]
push dword [eax] ; initialize [.sectors_todo]
and dword [eax], 0
push ebx esi edi ; save used registers to be stdcall
; 2. Acquire the global lock.
mov ecx, floppy_mutex
call mutex_lock
; 3. Set floppy number for this operation.
mov edx, [userdata]
mov [flp_number], dl
; 4. Read/write sector-by-sector.
.operation_loop:
; 4a. Check that the sector is inside the media.
cmp dword [start_sector+4], 0
jnz .end_of_media
mov eax, dword [start_sector]
cmp eax, FLOPPY_CAPACITY
jae .end_of_media
; 4b. For read operation, call read_chs_sector and then move data from FDD_BUFF to [buffer].
; For write operation, move data from [buffer] to FDD_BUFF and then call save_chs_sector.
cmp [.operation], 0
jz .read
mov esi, [buffer]
mov edi, FDD_BUFF
mov ecx, 512/4
rep movsd
mov [buffer], esi
call save_chs_sector
jmp @f
.read:
call read_chs_sector
mov esi, FDD_BUFF
mov edi, [buffer]
mov ecx, 512/4
rep movsd
mov [buffer], edi
@@:
; 4c. If there was an error, propagate it to the caller.
cmp [FDC_Status], 0
jnz .fail
; 4d. Otherwise, increment number of sectors processed and continue the loop.
mov eax, [numsectors_ptr]
inc dword [eax]
inc dword [start_sector]
dec [.sectors_todo]
jnz .operation_loop
; 5. Release the global lock and return with the correct status.
push 0
.return:
mov ecx, floppy_mutex
call mutex_unlock
pop eax
pop edi esi ebx ; restore used registers to be stdcall
ret ; this translates to leave/retn N and purges local variables
.fail:
push -1
jmp .return
.end_of_media:
push DISK_STATUS_END_OF_MEDIA
jmp .return
endp
/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc
47,6 → 47,10
hd1_data HD_DATA ?, 0x10, 2
hd2_data HD_DATA ?, 0, 3
hd3_data HD_DATA ?, 0x10, 4
 
hd_address_table:
dd 0x1f0, 0x00, 0x1f0, 0x10
dd 0x170, 0x00, 0x170, 0x10
endg
 
uglobal
645,14 → 649,6
end if
ret
;-----------------------------------------------------------------------------
hd_lba_error:
if lang eq sp
DEBUGF 1,"K : FS - HD error en LBA\n"
else
DEBUGF 1,"K : FS - HD LBA error\n"
end if
jmp LBA_read_ret
;-----------------------------------------------------------------------------
align 4
wait_for_hd_idle:
push eax edx
/kernel/branches/Kolibri-acpi/blkdev/rd.inc
1,2296 → 1,195
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; RAMDISK functions ;;
;; (C) 2004 Ville Turjanmaa, License: GPL ;;
;; Addings by M.Lisovin ;;
;; LFN support by diamond ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
iglobal
align 4
ramdisk_functions:
dd .size
dd 0 ; no close() function
dd 0 ; no closemedia() function
dd ramdisk_querymedia
dd ramdisk_read
dd ramdisk_write
dd 0 ; no flush() function
dd ramdisk_adjust_cache_size
.size = $ - ramdisk_functions
endg
 
; calculate fat chain
; See memmap.inc.
; Currently size of memory allocated for the ramdisk is fixed.
; This should be revisited when/if memory map would become more dynamic.
RAMDISK_CAPACITY = 2880 ; in sectors
 
calculatefatchain:
 
pushad
 
mov esi, RAMDISK+512
mov edi, RAMDISK_FAT
 
fcnew:
mov eax, dword [esi]
mov ebx, dword [esi+4]
mov ecx, dword [esi+8]
mov edx, ecx
shr edx, 4;8 ok
shr dx, 4;7 ok
xor ch, ch
shld ecx, ebx, 20;6 ok
shr cx, 4;5 ok
shld ebx, eax, 12
and ebx, 0x0fffffff;4 ok
shr bx, 4;3 ok
shl eax, 4
and eax, 0x0fffffff;2 ok
shr ax, 4;1 ok
mov dword [edi], eax
mov dword [edi+4], ebx
mov dword [edi+8], ecx
mov dword [edi+12], edx
add edi, 16
add esi, 12
 
cmp edi, RAMDISK_FAT+2856*2;2849 clusters
jnz fcnew
 
popad
ret
 
 
restorefatchain: ; restore fat chain
 
pushad
 
mov esi, RAMDISK_FAT
mov edi, RAMDISK+512
 
fcnew2:
mov eax, dword [esi]
mov ebx, dword [esi+4]
shl ax, 4
shl eax, 4
shl bx, 4
shr ebx, 4
shrd eax, ebx, 8
shr ebx, 8
mov dword [edi], eax
mov word [edi+4], bx
add edi, 6
add esi, 8
 
cmp edi, RAMDISK+512+4278;4274 bytes - all used FAT
jb fcnew2
 
mov esi, RAMDISK+512 ; duplicate fat chain
mov edi, RAMDISK+512+0x1200
mov ecx, 1069;4274/4
cld
rep movsd
 
popad
ret
 
 
ramdisk_free_space:
;---------------------------------------------
;
; returns free space in edi
; rewr.by Mihasik
;---------------------------------------------
 
push eax ebx ecx
 
mov edi, RAMDISK_FAT;start of FAT
xor ax, ax;Free cluster=0x0000 in FAT
xor ebx, ebx;counter
mov ecx, 2849;2849 clusters
cld
rdfs1:
repne scasw
jnz rdfs2 ;if last cluster not 0
inc ebx
test ecx, ecx
jnz rdfs1
rdfs2:
shl ebx, 9;free clusters*512
mov edi, ebx
 
pop ecx ebx eax
ret
 
 
expand_filename:
;---------------------------------------------
;
; exapand filename with '.' to 11 character
; eax - pointer to filename
;---------------------------------------------
 
push esi edi ebx
 
mov edi, esp ; check for '.' in the name
add edi, 12+8
 
mov esi, eax
 
mov eax, edi
mov [eax+0], dword ' '
mov [eax+4], dword ' '
mov [eax+8], dword ' '
 
flr1:
 
cmp [esi], byte '.'
jne flr2
mov edi, eax
add edi, 7
jmp flr3
 
flr2:
 
mov bl, [esi]
mov [edi], bl
 
flr3:
 
inc esi
inc edi
 
mov ebx, eax
add ebx, 11
 
cmp edi, ebx
jbe flr1
 
pop ebx edi esi
ret
 
fileread:
;----------------------------------------------------------------
;
; fileread - sys floppy
;
; eax points to filename 11 chars
; ebx first wanted block ; 1+ ; if 0 then set to 1
; ecx number of blocks to read ; 1+ ; if 0 then set to 1
; edx mem location to return data
; esi length of filename 12*X 0=root
;
; ret ebx = size or 0xffffffff file not found
; eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
test ebx, ebx;if ebx=0 - set to 1
jnz frfl5
inc ebx
frfl5:
test ecx, ecx;if ecx=0 - set to 1
jnz frfl6
inc ecx
frfl6:
test esi, esi ; return ramdisk root
jnz fr_noroot ;if not root
cmp ebx, 14 ;14 clusters=root dir
ja oorr
cmp ecx, 14
ja oorr
jmp fr_do
oorr:
mov eax, 5 ;out of root range (fnf)
xor ebx, ebx
dec ebx ;0xffffffff
ret
 
fr_do: ;reading rootdir
mov edi, edx
dec ebx
push edx
mov edx, ecx
add edx, ebx
cmp edx, 15 ;ebx+ecx=14+1
pushf
jbe fr_do1
sub edx, 14
sub ecx, edx
fr_do1:
shl ebx, 9
mov esi, RAMDISK+512*19
add esi, ebx
shl ecx, 7
cld
rep movsd
popf
pop edx
jae fr_do2
xor eax, eax; ok read
xor ebx, ebx
ret
fr_do2: ;if last cluster
mov eax, 6;end of file
xor ebx, ebx
ret
 
fr_noroot:
 
sub esp, 32
call expand_filename
 
dec ebx
 
push eax
 
push eax ebx ecx edx esi edi
call rd_findfile
je fifound
add esp, 32+28 ;if file not found
ret
 
fifound:
 
mov ebx, [edi-11+28] ;file size
mov [esp+20], ebx
mov [esp+24], ebx
add edi, 0xf
movzx eax, word [edi]
mov edi, eax ;edi=cluster
 
frnew:
 
add eax, 31 ;bootsector+2*fat+filenames
shl eax, 9 ;*512
add eax, RAMDISK ;image base
mov ebx, [esp+8]
mov ecx, 512 ;[esp+4]
 
cmp [esp+16], dword 0 ; wanted cluster ?
jne frfl7
call memmove
add [esp+8], dword 512
dec dword [esp+12] ; last wanted cluster ?
je frnoread
jmp frfl8
frfl7:
dec dword [esp+16]
frfl8:
movzx eax, word [edi*2+RAMDISK_FAT] ; find next cluster from FAT
mov edi, eax
cmp edi, 4095 ;eof - cluster
jz frnoread2
 
cmp [esp+24], dword 512 ;eof - size
jb frnoread
sub [esp+24], dword 512
 
jmp frnew
 
frnoread2:
 
cmp [esp+16], dword 0 ; eof without read ?
je frnoread
 
pop edi esi edx ecx
add esp, 4
pop ebx ; ebx <- eax : size of file
add esp, 36
mov eax, 6 ; end of file
ret
 
frnoread:
 
pop edi esi edx ecx
add esp, 4
pop ebx ; ebx <- eax : size of file
add esp, 36
xor eax, eax;read ok
ret
 
 
 
rd_findfile:
;by Mihasik
;IN: eax - pointer to filename OUT: filestring+11 in edi or notZero in flags and fnf in eax,ebx
 
mov edi, RAMDISK+512*18+512;Point at directory
cld
rd_newsearch:
mov esi, eax
mov ecx, 11
rep cmpsb
je rd_ff
add cl, 21
add edi, ecx
cmp edi, RAMDISK+512*33
jb rd_newsearch
mov eax, 5 ;if file not found - eax=5
xor ebx, ebx
dec ebx ;ebx=0xffffffff and zf=0
rd_ff:
ret
 
; \begin{diamond}
 
uni2ansi_str:
; convert UNICODE zero-terminated string to ASCII-string (codepage 866)
; in: esi->source, edi->buffer (may be esi=edi)
; destroys: eax,esi,edi
lodsw
test ax, ax
jz .done
cmp ax, 0x80
jb .ascii
cmp ax, 0x401
jz .yo1
cmp ax, 0x451
jz .yo2
cmp ax, 0x410
jb .unk
cmp ax, 0x440
jb .rus1
cmp ax, 0x450
jb .rus2
.unk:
mov al, '_'
jmp .doit
.yo1:
mov al, 0xF0 ; 'Ё'
jmp .doit
.yo2:
mov al, 0xF1 ; 'ё'
jmp .doit
.rus1:
; 0x410-0x43F -> 0x80-0xAF
add al, 0x70
jmp .doit
.rus2:
; 0x440-0x44F -> 0xE0-0xEF
add al, 0xA0
.ascii:
.doit:
stosb
jmp uni2ansi_str
.done:
mov byte [edi], 0
ret
 
ansi2uni_char:
; convert ANSI character in al to UNICODE character in ax, using cp866 encoding
mov ah, 0
; 0x00-0x7F - trivial map
cmp al, 0x80
jb .ret
; 0x80-0xAF -> 0x410-0x43F
cmp al, 0xB0
jae @f
add ax, 0x410-0x80
.ret:
ret
@@:
; 0xE0-0xEF -> 0x440-0x44F
cmp al, 0xE0
jb .unk
cmp al, 0xF0
jae @f
add ax, 0x440-0xE0
ret
; 0xF0 -> 0x401
; 0xF1 -> 0x451
@@:
cmp al, 0xF0 ; 'Ё'
jz .yo1
cmp al, 0xF1 ; 'ё'
jz .yo2
.unk:
mov al, '_' ; ah=0
ret
.yo1:
mov ax, 0x401
ret
.yo2:
mov ax, 0x451
ret
 
char_toupper:
; convert character to uppercase, using cp866 encoding
; in: al=symbol
; out: al=converted symbol
cmp al, 'a'
jb .ret
cmp al, 'z'
jbe .az
cmp al, 0xF1 ; 'ё'
jz .yo1
cmp al, 0xA0 ; 'а'
jb .ret
cmp al, 0xE0 ; 'р'
jb .rus1
cmp al, 0xEF ; 'я'
ja .ret
; 0xE0-0xEF -> 0x90-0x9F
sub al, 0xE0-0x90
.ret:
ret
.rus1:
; 0xA0-0xAF -> 0x80-0x8F
.az:
and al, not 0x20
ret
.yo1:
; 0xF1 -> 0xF0
dec ax
ret
 
fat_get_name:
; in: edi->FAT entry
; out: CF=1 - no valid entry
; else CF=0 and ebp->ASCIIZ-name
; (maximum length of filename is 255 (wide) symbols without trailing 0,
; but implementation requires buffer 261 words)
; destroys eax
cmp byte [edi], 0
jz .no
cmp byte [edi], 0xE5
jnz @f
.no:
stc
ret
@@:
cmp byte [edi+11], 0xF
jz .longname
test byte [edi+11], 8
jnz .no
push ecx
push edi ebp
test byte [ebp-4], 1
jnz .unicode_short
 
mov eax, [edi]
mov ecx, [edi+4]
mov [ebp], eax
mov [ebp+4], ecx
 
mov ecx, 8
@@:
cmp byte [ebp+ecx-1], ' '
loope @b
 
mov eax, [edi+8]
cmp al, ' '
je .done
shl eax, 8
mov al, '.'
 
lea ebp, [ebp+ecx+1]
mov [ebp], eax
mov ecx, 3
@@:
rol eax, 8
cmp al, ' '
jne .done
loop @b
dec ebp
.done:
and byte [ebp+ecx+1], 0 ; CF=0
pop ebp edi ecx
ret
.unicode_short:
mov ecx, 8
push ecx
@@:
mov al, [edi]
inc edi
call ansi2uni_char
mov [ebp], ax
inc ebp
inc ebp
loop @b
pop ecx
@@:
cmp word [ebp-2], ' '
jnz @f
dec ebp
dec ebp
loop @b
@@:
mov word [ebp], '.'
inc ebp
inc ebp
mov ecx, 3
push ecx
@@:
mov al, [edi]
inc edi
call ansi2uni_char
mov [ebp], ax
inc ebp
inc ebp
loop @b
pop ecx
@@:
cmp word [ebp-2], ' '
jnz @f
dec ebp
dec ebp
loop @b
dec ebp
dec ebp
@@:
and word [ebp], 0 ; CF=0
pop ebp edi ecx
ret
.longname:
; LFN
mov al, byte [edi]
and eax, 0x3F
dec eax
cmp al, 20
jae .no ; ignore invalid entries
mov word [ebp+260*2], 0 ; force null-terminating for orphans
imul eax, 13*2
add ebp, eax
test byte [edi], 0x40
jz @f
mov word [ebp+13*2], 0
@@:
push eax
; now copy name from edi to ebp ...
mov eax, [edi+1]
mov [ebp], eax ; symbols 1,2
mov eax, [edi+5]
mov [ebp+4], eax ; 3,4
mov eax, [edi+9]
mov [ebp+8], ax ; 5
mov eax, [edi+14]
mov [ebp+10], eax ; 6,7
mov eax, [edi+18]
mov [ebp+14], eax ; 8,9
mov eax, [edi+22]
mov [ebp+18], eax ; 10,11
mov eax, [edi+28]
mov [ebp+22], eax ; 12,13
; ... done
pop eax
sub ebp, eax
test eax, eax
jz @f
; if this is not first entry, more processing required
stc
ret
@@:
; if this is first entry:
test byte [ebp-4], 1
jnz .ret
; buffer at ebp contains UNICODE name, convert it to ANSI
push esi edi
mov esi, ebp
mov edi, ebp
call uni2ansi_str
pop edi esi
.ret:
clc
ret
 
fat_compare_name:
; compares ASCIIZ-names, case-insensitive (cp866 encoding)
; in: esi->name, ebp->name
; out: if names match: ZF=1 and esi->next component of name
; else: ZF=0, esi is not changed
; destroys eax
push ebp esi
.loop:
mov al, [ebp]
inc ebp
call char_toupper
push eax
lodsb
call char_toupper
cmp al, [esp]
jnz .done
pop eax
test al, al
jnz .loop
dec esi
pop eax
pop ebp
xor eax, eax ; set ZF flag
ret
.done:
cmp al, '/'
jnz @f
cmp byte [esp], 0
jnz @f
mov [esp+4], esi
@@:
pop eax
pop esi ebp
ret
 
fat_time_to_bdfe:
; in: eax=FAT time
; out: eax=BDFE time
push ecx edx
mov ecx, eax
mov edx, eax
shr eax, 11
shl eax, 16 ; hours
and edx, 0x1F
add edx, edx
mov al, dl ; seconds
shr ecx, 5
and ecx, 0x3F
mov ah, cl ; minutes
pop edx ecx
ret
 
fat_date_to_bdfe:
push ecx edx
mov ecx, eax
mov edx, eax
shr eax, 9
add ax, 1980
shl eax, 16 ; year
and edx, 0x1F
mov al, dl ; day
shr ecx, 5
and ecx, 0xF
mov ah, cl ; month
pop edx ecx
ret
 
bdfe_to_fat_time:
push edx
mov edx, eax
shr eax, 16
and dh, 0x3F
shl eax, 6
or al, dh
shr dl, 1
and dl, 0x1F
shl eax, 5
or al, dl
pop edx
ret
 
bdfe_to_fat_date:
push edx
mov edx, eax
shr eax, 16
sub ax, 1980
and dh, 0xF
shl eax, 4
or al, dh
and dl, 0x1F
shl eax, 5
or al, dl
pop edx
ret
 
fat_entry_to_bdfe:
; convert FAT entry at edi to BDFE (block of data of folder entry) at esi, advance esi
; destroys eax
mov eax, [ebp-4]
mov [esi+4], eax ; ASCII/UNICODE name
fat_entry_to_bdfe2:
movzx eax, byte [edi+11]
mov [esi], eax ; attributes
movzx eax, word [edi+14]
call fat_time_to_bdfe
mov [esi+8], eax ; creation time
movzx eax, word [edi+16]
call fat_date_to_bdfe
mov [esi+12], eax ; creation date
and dword [esi+16], 0 ; last access time is not supported on FAT
movzx eax, word [edi+18]
call fat_date_to_bdfe
mov [esi+20], eax ; last access date
movzx eax, word [edi+22]
call fat_time_to_bdfe
mov [esi+24], eax ; last write time
movzx eax, word [edi+24]
call fat_date_to_bdfe
mov [esi+28], eax ; last write date
mov eax, [edi+28]
mov [esi+32], eax ; file size (low dword)
xor eax, eax
mov [esi+36], eax ; file size (high dword)
test ebp, ebp
jz .ret
push ecx edi
lea edi, [esi+40]
mov esi, ebp
test byte [esi-4], 1
jz .ansi
mov ecx, 260/2
rep movsd
mov [edi-2], ax
@@:
mov esi, edi
pop edi ecx
.ret:
ret
.ansi:
mov ecx, 264/4
rep movsd
mov [edi-1], al
jmp @b
 
bdfe_to_fat_entry:
; convert BDFE at edx to FAT entry at edi
; destroys eax
; attributes byte
test byte [edi+11], 8 ; volume label?
jnz @f
mov al, [edx]
and al, 0x27
and byte [edi+11], 0x10
or byte [edi+11], al
@@:
mov eax, [edx+8]
call bdfe_to_fat_time
mov [edi+14], ax ; creation time
mov eax, [edx+12]
call bdfe_to_fat_date
mov [edi+16], ax ; creation date
mov eax, [edx+20]
call bdfe_to_fat_date
mov [edi+18], ax ; last access date
mov eax, [edx+24]
call bdfe_to_fat_time
mov [edi+22], ax ; last write time
mov eax, [edx+28]
call bdfe_to_fat_date
mov [edi+24], ax ; last write date
ret
 
ramdisk_root_first:
mov edi, RAMDISK+512*19
clc
ret
ramdisk_root_next:
add edi, 0x20
cmp edi, RAMDISK+512*33
cmc
ret
 
ramdisk_root_extend_dir:
stc
ret
 
uglobal
; this is for delete support
rd_prev_sector dd ?
rd_prev_prev_sector dd ?
iglobal
align 4
ramdisk_actual_size dd RAMDISK_CAPACITY
endg
 
ramdisk_notroot_next:
add edi, 0x20
test edi, 0x1FF
jz ramdisk_notroot_next_sector
ret ; CF=0
ramdisk_notroot_next_sector:
push ecx
mov ecx, [eax]
push [rd_prev_sector]
pop [rd_prev_prev_sector]
mov [rd_prev_sector], ecx
mov ecx, [ecx*2+RAMDISK_FAT]
and ecx, 0xFFF
cmp ecx, 2849
jae ramdisk_notroot_first.err2
mov [eax], ecx
pop ecx
ramdisk_notroot_first:
mov eax, [eax]
cmp eax, 2
jb .err
cmp eax, 2849
jae .err
shl eax, 9
lea edi, [eax+(31 shl 9)+RAMDISK]
clc
ret
.err2:
pop ecx
.err:
stc
ret
ramdisk_notroot_next_write:
test edi, 0x1FF
jz ramdisk_notroot_next_sector
ramdisk_root_next_write:
ret
 
ramdisk_notroot_extend_dir:
pusha
xor eax, eax
mov edi, RAMDISK_FAT
mov ecx, 2849
repnz scasw
jnz .notfound
mov word [edi-2], 0xFFF
sub edi, RAMDISK_FAT
shr edi, 1
dec edi
mov eax, [esp+28]
mov ecx, [eax]
mov [RAMDISK_FAT+ecx*2], di
mov [eax], edi
shl edi, 9
add edi, (31 shl 9)+RAMDISK
mov [esp], edi
xor eax, eax
mov ecx, 128
rep stosd
popa
clc
ret
.notfound:
popa
stc
ret
 
rd_find_lfn:
; in: esi+ebp -> name
; out: CF=1 - file not found
; else CF=0 and edi->direntry
push esi edi
push 0
push ramdisk_root_first
push ramdisk_root_next
.loop:
call fat_find_lfn
jc .notfound
cmp byte [esi], 0
jz .found
.continue:
test byte [edi+11], 10h
jz .notfound
movzx eax, word [edi+26]
mov [esp+8], eax
mov dword [esp+4], ramdisk_notroot_first
mov dword [esp], ramdisk_notroot_next
test eax, eax
jnz .loop
mov dword [esp+4], ramdisk_root_first
mov dword [esp], ramdisk_notroot_next
jmp .loop
.notfound:
add esp, 12
pop edi esi
stc
ret
.found:
test ebp, ebp
jz @f
mov esi, ebp
xor ebp, ebp
jmp .continue
@@:
mov eax, [esp+8]
add esp, 16 ; CF=0
pop esi
ret
 
;----------------------------------------------------------------
;
; fs_RamdiskRead - LFN variant for reading sys floppy
;
; esi points to filename
; ebx pointer to 64-bit number = first wanted byte, 0+
; may be ebx=0 - start from first byte
; ecx number of bytes to read, 0+
; edx mem location to return data
;
; ret ebx = bytes read or 0xffffffff file not found
; eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
fs_RamdiskRead:
cmp byte [esi], 0
jnz @f
or ebx, -1
mov eax, 10 ; access denied
ret
@@:
push edi
call rd_find_lfn
jnc .found
pop edi
or ebx, -1
mov eax, 5 ; file not found
ret
.found:
test ebx, ebx
jz .l1
cmp dword [ebx+4], 0
jz @f
xor ebx, ebx
.reteof:
mov eax, 6 ; EOF
pop edi
ret
@@:
mov ebx, [ebx]
.l1:
push ecx edx
push 0
mov eax, [edi+28]
sub eax, ebx
jb .eof
cmp eax, ecx
jae @f
mov ecx, eax
mov byte [esp], 6 ; EOF
@@:
movzx edi, word [edi+26] ; cluster
.new:
jecxz .done
test edi, edi
jz .eof
cmp edi, 0xFF8
jae .eof
lea eax, [edi+31] ; bootsector+2*fat+filenames
shl eax, 9 ; *512
add eax, RAMDISK ; image base
; now eax points to data of cluster
sub ebx, 512
jae .skip
lea eax, [eax+ebx+512]
neg ebx
push ecx
cmp ecx, ebx
jbe @f
mov ecx, ebx
@@:
mov ebx, edx
call memmove
add edx, ecx
sub [esp], ecx
pop ecx
xor ebx, ebx
.skip:
movzx edi, word [edi*2+RAMDISK_FAT] ; find next cluster from FAT
jmp .new
.eof:
mov ebx, edx
pop eax edx ecx
sub ebx, edx
jmp .reteof
.done:
mov ebx, edx
pop eax edx ecx edi
sub ebx, edx
ret
 
;----------------------------------------------------------------
;
; fs_RamdiskReadFolder - LFN variant for reading sys floppy folder
;
; esi points to filename; only root is folder on ramdisk
; ebx pointer to structure 32-bit number = first wanted block
; & flags (bitfields)
; flags: bit 0: 0=ANSI names, 1=UNICODE names
; ecx number of blocks to read, 0+
; edx mem location to return data
;
; ret ebx = size or 0xffffffff file not found
; eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
fs_RamdiskReadFolder:
push edi
cmp byte [esi], 0
jz .root
call rd_find_lfn
jnc .found
pop edi
or ebx, -1
mov eax, ERROR_FILE_NOT_FOUND
ret
.found:
test byte [edi+11], 0x10
jnz .found_dir
pop edi
or ebx, -1
mov eax, ERROR_ACCESS_DENIED
ret
.found_dir:
movzx eax, word [edi+26]
add eax, 31
push 0
jmp .doit
.root:
mov eax, 19
push 14
.doit:
push esi ecx ebp
sub esp, 262*2 ; reserve space for LFN
mov ebp, esp
push dword [ebx+4] ; for fat_get_name: read ANSI/UNICODE names
mov ebx, [ebx]
; init header
push eax ecx
mov edi, edx
mov ecx, 32/4
xor eax, eax
rep stosd
mov byte [edx], 1 ; version
pop ecx eax
mov esi, edi ; esi points to block of data of folder entry (BDFE)
.main_loop:
mov edi, eax
shl edi, 9
add edi, RAMDISK
push eax
.l1:
call fat_get_name
jc .l2
cmp byte [edi+11], 0xF
jnz .do_bdfe
add edi, 0x20
test edi, 0x1FF
jnz .do_bdfe
pop eax
inc eax
dec byte [esp+262*2+16]
jz .done
jns @f
; read next sector from FAT
mov eax, [(eax-31-1)*2+RAMDISK_FAT]
and eax, 0xFFF
cmp eax, 0xFF8
jae .done
add eax, 31
mov byte [esp+262*2+16], 0
@@:
mov edi, eax
shl edi, 9
add edi, RAMDISK
push eax
.do_bdfe:
inc dword [edx+8] ; new file found
dec ebx
jns .l2
dec ecx
js .l2
inc dword [edx+4] ; new file block copied
call fat_entry_to_bdfe
.l2:
add edi, 0x20
test edi, 0x1FF
jnz .l1
pop eax
inc eax
dec byte [esp+262*2+16]
jz .done
jns @f
; read next sector from FAT
mov eax, [(eax-31-1)*2+RAMDISK_FAT]
and eax, 0xFFF
cmp eax, 0xFF8
jae .done
add eax, 31
mov byte [esp+262*2+16], 0
@@:
jmp .main_loop
.done:
add esp, 262*2+4
pop ebp
mov ebx, [edx+4]
xor eax, eax
dec ecx
js @f
mov al, ERROR_END_OF_FILE
@@:
pop ecx esi edi edi
ret
 
; This function is called early in boot process.
; It creates filesystem /rd/1 based on raw image data loaded by somebody before
; to memory named as RAMDISK with max size RAMDISK_CAPACITY, may be less.
proc ramdisk_init
iglobal
label fat_legal_chars byte
; 0 = not allowed
; 1 = allowed only in long names
; 3 = allowed
times 32 db 0
; ! " # $ % & ' ( ) * + , - . /
db 1,3,0,3,3,3,3,3,3,3,0,1,1,3,3,0
; 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
db 3,3,3,3,3,3,3,3,3,3,0,1,0,1,0,0
; @ A B C D E F G H I J K L M N O
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; P Q R S T U V W X Y Z [ \ ] ^ _
db 3,3,3,3,3,3,3,3,3,3,3,1,0,1,3,3
; ` a b c d e f g h i j k l m n o
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; p q r s t u v w x y z { | } ~
db 3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0
ramdisk_name db 'rd',0
endg
 
fat_name_is_legal:
; in: esi->(long) name
; out: CF set <=> legal
; destroys eax
push esi
xor eax, eax
@@:
lodsb
test al, al
jz .done
cmp al, 80h
jae .big
test [fat_legal_chars+eax], 1
jnz @b
.err:
pop esi
clc
ret
.big:
; 0x80-0xAF, 0xE0-0xEF
cmp al, 0xB0
jb @b
cmp al, 0xE0
jb .err
cmp al, 0xF0
jb @b
jmp .err
.done:
sub esi, [esp]
cmp esi, 257
pop esi
ret
 
fat_next_short_name:
; in: edi->8+3 name
; out: name corrected
; CF=1 <=> error
pushad
mov ecx, 8
mov al, '~'
std
push edi
add edi, 7
repnz scasb
pop edi
cld
jz .tilde
; tilde is not found, insert "~1" at end
add edi, 6
cmp word [edi], ' '
jnz .insert_tilde
@@:
dec edi
cmp byte [edi], ' '
jz @b
inc edi
.insert_tilde:
mov word [edi], '~1'
popad
clc
ret
.tilde:
push edi
add edi, 7
push ebx esi ; save used registers to be stdcall
; 1. Register the device and the (always inserted) media in the disk subsystem.
stdcall disk_add, ramdisk_functions, ramdisk_name, 0, 0
test eax, eax
jz .fail
mov ebx, eax
stdcall disk_media_changed, eax, 1
; 2. We don't know actual size of loaded image,
; so try to calculate it using partition structure,
; assuming that file systems fill the real size based on contents of the partition.
; 2a. Prepare for loop over partitions.
xor ecx, ecx
xor edx, edx
; 2b. Check that at least one partition was recognized.
cmp [ebx+DISK.NumPartitions], ecx
jz .fail
; 2c. Loop over partitions.
.partitions:
; For every partition, set edx to maximum between edx and end of partition.
mov esi, [ebx+DISK.Partitions]
mov esi, [esi+ecx*4]
mov eax, dword [esi+PARTITION.FirstSector]
add eax, dword [esi+PARTITION.Length]
cmp eax, edx
jb @f
mov edx, eax
@@:
; after tilde may be only digits and trailing spaces
cmp byte [edi], '~'
jz .break
cmp byte [edi], ' '
jz .space
cmp byte [edi], '9'
jnz .found
dec edi
jmp @b
.space:
dec edi
inc ecx
jmp @b
.found:
inc byte [edi]
add dword [esp], 8
jmp .zerorest
.break:
jecxz .noplace
inc edi
mov al, '1'
cmp ecx, [ebx+DISK.NumPartitions]
jb .partitions
; 3. Reclaim unused memory, if any.
mov [ramdisk_actual_size], edx
add edx, 7 ; aligning up
shr edx, 3 ; 512-byte sectors -> 4096-byte pages
mov esi, RAMDISK_CAPACITY / 8 ; aligning down
sub esi, edx
jbe .no_reclaim
shl edx, 12
add edx, RAMDISK - OS_BASE
@@:
xchg al, [edi]
inc edi
cmp al, ' '
mov al, '0'
mov eax, edx
call free_page
add edx, 0x1000
dec esi
jnz @b
.succ:
pop edi
popad
clc
.no_reclaim:
pop esi ebx ; restore used registers to be stdcall
ret
.noplace:
dec edi
cmp edi, [esp]
jz .err
add dword [esp], 8
mov word [edi], '~1'
inc edi
inc edi
@@:
mov byte [edi], '0'
.zerorest:
inc edi
cmp edi, [esp]
jb @b
pop edi
popad
;clc ; automatically
.fail:
dbgstr 'Failed to initialize ramdisk'
pop esi ebx ; restore used registers to be stdcall
ret
.err:
pop edi
popad
stc
ret
endp
 
fat_gen_short_name:
; in: esi->long name
; edi->buffer (8+3=11 chars)
; out: buffer filled
pushad
mov eax, ' '
push edi
stosd
stosd
stosd
pop edi
; Returns information about disk media.
proc ramdisk_querymedia
virtual at esp+4
.userdata dd ?
.info dd ?
end virtual
; Media is always present, sector size is always 512 bytes.
mov edx, [.userdata]
mov ecx, [.info]
mov [ecx+DISKMEDIAINFO.Flags], 0
mov [ecx+DISKMEDIAINFO.SectorSize], 512
mov eax, [ramdisk_actual_size]
mov dword [ecx+DISKMEDIAINFO.Capacity], eax
mov dword [ecx+DISKMEDIAINFO.Capacity+4], 0
; Return zero as an indicator of success.
xor eax, eax
movi ebx, 8
lea ecx, [edi+8]
.loop:
lodsb
test al, al
jz .done
call char_toupper
cmp al, ' '
jz .space
cmp al, 80h
ja .big
test [fat_legal_chars+eax], 2
jnz .symbol
.inv_symbol:
mov al, '_'
or bh, 1
.symbol:
cmp al, '.'
jz .dot
.normal_symbol:
dec bl
jns .store
mov bl, 0
.space:
or bh, 1
jmp .loop
.store:
stosb
jmp .loop
.big:
cmp al, 0xB0
jb .normal_symbol
cmp al, 0xE0
jb .inv_symbol
cmp al, 0xF0
jb .normal_symbol
jmp .inv_symbol
.dot:
test bh, 2
jz .firstdot
pop ebx
add ebx, edi
sub ebx, ecx
push ebx
cmp ebx, ecx
jb @f
pop ebx
push ecx
@@:
cmp edi, ecx
jbe .skip
@@:
dec edi
mov al, [edi]
dec ebx
mov [ebx], al
mov byte [edi], ' '
cmp edi, ecx
ja @b
.skip:
mov bh, 3
jmp @f
.firstdot:
cmp bl, 8
jz .space
push edi
or bh, 2
@@:
mov edi, ecx
mov bl, 3
jmp .loop
.done:
test bh, 2
jz @f
pop edi
@@:
lea edi, [ecx-8]
test bh, 1
jz @f
call fat_next_short_name
@@:
popad
ret
retn 8
endp
 
;----------------------------------------------------------------
;
; fs_RamdiskRewrite - LFN variant for writing ramdisk
; fs_RamdiskCreateFolder - create folder on ramdisk
;
; esi points to file/folder name
; ebx ignored (reserved)
; ecx number of bytes to write, 0+ (ignored for folders)
; edx mem location to data (ignored for folders)
;
; ret ebx = number of written bytes
; eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
@@:
mov eax, ERROR_ACCESS_DENIED
xor ebx, ebx
ret
 
fs_RamdiskCreateFolder:
mov al, 1 ; create folder
jmp fs_RamdiskRewrite.common
 
fs_RamdiskRewrite:
xor eax, eax ; create file
.common:
cmp byte [esi], 0
jz @b
pushad
xor edi, edi
push esi
test ebp, ebp
jz @f
mov esi, ebp
@@:
lodsb
test al, al
jz @f
cmp al, '/'
jnz @b
lea edi, [esi-1]
jmp @b
@@:
pop esi
test edi, edi
jnz .noroot
test ebp, ebp
jnz .hasebp
push ramdisk_root_extend_dir
push ramdisk_root_next_write
push edi
push ramdisk_root_first
push ramdisk_root_next
jmp .common1
.hasebp:
mov eax, ERROR_ACCESS_DENIED
cmp byte [ebp], 0
jz .ret1
push ebp
xor ebp, ebp
call rd_find_lfn
pop esi
jc .notfound0
jmp .common0
.noroot:
mov eax, ERROR_ACCESS_DENIED
cmp byte [edi+1], 0
jz .ret1
; check existence
mov byte [edi], 0
push edi
call rd_find_lfn
pop esi
mov byte [esi], '/'
jnc @f
.notfound0:
mov eax, ERROR_FILE_NOT_FOUND
.ret1:
mov [esp+28], eax
popad
xor ebx, ebx
ret
@@:
inc esi
.common0:
test byte [edi+11], 0x10 ; must be directory
mov eax, ERROR_ACCESS_DENIED
jz .ret1
movzx ebp, word [edi+26] ; ebp=cluster
mov eax, ERROR_FAT_TABLE
cmp ebp, 2
jb .ret1
cmp ebp, 2849
jae .ret1
push ramdisk_notroot_extend_dir
push ramdisk_notroot_next_write
push ebp
push ramdisk_notroot_first
push ramdisk_notroot_next
.common1:
call fat_find_lfn
jc .notfound
; found
test byte [edi+11], 10h
jz .exists_file
; found directory; if we are creating directory, return OK,
; if we are creating file, say "access denied"
add esp, 20
popad
test al, al
mov eax, ERROR_ACCESS_DENIED
jz @f
mov al, 0
@@:
xor ebx, ebx
ret
.exists_file:
; found file; if we are creating directory, return "access denied",
; if we are creating file, delete existing file and continue
cmp byte [esp+20+28], 0
jz @f
add esp, 20
popad
mov eax, ERROR_ACCESS_DENIED
xor ebx, ebx
ret
@@:
; delete FAT chain
push edi
xor eax, eax
mov dword [edi+28], eax ; zero size
xchg ax, word [edi+26] ; start cluster
test eax, eax
jz .done1
@@:
cmp eax, 0xFF8
jae .done1
lea edi, [RAMDISK_FAT + eax*2] ; position in FAT
xor eax, eax
xchg ax, [edi]
jmp @b
.done1:
pop edi
call get_time_for_file
mov [edi+22], ax
call get_date_for_file
mov [edi+24], ax
mov [edi+18], ax
or byte [edi+11], 20h ; set 'archive' attribute
jmp .doit
.notfound:
; file is not found; generate short name
call fat_name_is_legal
jc @f
add esp, 20
popad
mov eax, ERROR_FILE_NOT_FOUND
xor ebx, ebx
ret
@@:
sub esp, 12
mov edi, esp
call fat_gen_short_name
.test_short_name_loop:
push esi edi ecx
mov esi, edi
lea eax, [esp+12+12+8]
mov [eax], ebp
call dword [eax-4]
jc .found
.test_short_name_entry:
cmp byte [edi+11], 0xF
jz .test_short_name_cont
mov ecx, 11
push esi edi
repz cmpsb
pop edi esi
jz .short_name_found
.test_short_name_cont:
lea eax, [esp+12+12+8]
call dword [eax-8]
jnc .test_short_name_entry
jmp .found
.short_name_found:
pop ecx edi esi
call fat_next_short_name
jnc .test_short_name_loop
.disk_full:
add esp, 12+20
popad
mov eax, ERROR_DISK_FULL
xor ebx, ebx
ret
.found:
pop ecx edi esi
; now find space in directory
; we need to save LFN <=> LFN is not equal to short name <=> generated name contains '~'
mov al, '~'
push ecx edi
mov ecx, 8
repnz scasb
movi eax, 1 ; 1 entry
jnz .notilde
; we need ceil(strlen(esi)/13) additional entries = floor((strlen(esi)+12+13)/13) total
xor eax, eax
@@:
cmp byte [esi], 0
jz @f
inc esi
inc eax
jmp @b
@@:
sub esi, eax
add eax, 12+13
mov ecx, 13
push edx
cdq
div ecx
pop edx
.notilde:
push -1
push -1
; find <eax> successive entries in directory
; Common procedure for reading and writing.
; operation = 0 for reading, operation = 1 for writing.
; Arguments of ramdisk_read and ramdisk_write are the same.
macro ramdisk_read_write operation
{
push esi edi ; save used registers to be stdcall
mov esi, [userdata]
mov edi, [numsectors_ptr]
; 1. Determine number of sectors to be transferred.
; This is either the requested number of sectors or number of sectors
; up to the disk boundary, depending of what is less.
xor ecx, ecx
push eax
lea eax, [esp+12+8+12+8]
mov [eax], ebp
call dword [eax-4]
pop eax
.scan_dir:
cmp byte [edi], 0
jz .free
cmp byte [edi], 0xE5
jz .free
xor ecx, ecx
.scan_cont:
push eax
lea eax, [esp+12+8+12+8]
call dword [eax-8]
pop eax
jnc .scan_dir
push eax
lea eax, [esp+12+8+12+8]
call dword [eax+8] ; extend directory
pop eax
jnc .scan_dir
add esp, 8+8+12+20
popad
mov eax, ERROR_DISK_FULL
xor ebx, ebx
ret
.free:
test ecx, ecx
jnz @f
mov [esp], edi
mov ecx, [esp+8+8+12+8]
mov [esp+4], ecx
xor ecx, ecx
@@:
inc ecx
; 1a. Test whether [start_sector] is less than RAMDISK_CAPACITY.
; If so, calculate number of sectors between [start_sector] and RAMDISK_CAPACITY.
; Otherwise, the actual number of sectors is zero.
cmp dword [start_sector+4], ecx
jnz .got_number
mov eax, [ramdisk_actual_size]
sub eax, dword [start_sector]
jbe .got_number
; 1b. Get the requested number of sectors.
mov ecx, [edi]
; 1c. If it is greater than number of sectors calculated in 1a, use the value
; from 1a.
cmp ecx, eax
jb .scan_cont
; found!
; If creating a directory, allocate one data cluster now and fail immediately
; if this is impossible. This prevents from creating an invalid directory entry
; on a full disk.
; yup, the argument is quite non-intuitive... but what should I do if
; the entire function uses such arguments? BTW, it refers to al from pushad,
; which in turn is filled with 0 in fs_RamdiskRewrite and 1 in fs_RamdiskCreateFolder.
push esi ecx
cmp byte [esp+24+12+20+28], 0
jz .no.preallocate.folder.data
mov ecx, 2849
mov edi, RAMDISK_FAT
jb .got_number
mov ecx, eax
.got_number:
; 2. Compare the actual number of sectors with requested. If they are
; equal, set eax (it will be the returned value) to zero. Otherwise,
; use DISK_STATUS_END_OF_MEDIA.
xor eax, eax
repnz scasw
cmp ecx, [edi]
jz @f
add esp, 24
jmp .disk_full
mov al, DISK_STATUS_END_OF_MEDIA
@@:
mov [esp+24+12+20+20], edi ; store the cluster somewhere
.no.preallocate.folder.data:
; calculate name checksum
mov esi, [esp+8+8]
mov ecx, 11
xor eax, eax
@@:
ror al, 1
add al, [esi]
inc esi
loop @b
pop ecx esi
pop edi
pop dword [esp+8+12+8]
; edi points to last entry in free chunk
dec ecx
jz .nolfn
push esi
push eax
mov al, 40h
.writelfn:
or al, cl
mov esi, [esp+4]
push ecx
dec ecx
imul ecx, 13
add esi, ecx
stosb
mov cl, 5
call .read_symbols
mov ax, 0xF
stosw
mov al, [esp+4]
stosb
mov cl, 6
call .read_symbols
xor eax, eax
stosw
mov cl, 2
call .read_symbols
pop ecx
lea eax, [esp+8+8+12+8]
call dword [eax+4] ; next write
xor eax, eax
loop .writelfn
pop eax
pop esi
.nolfn:
xchg esi, [esp]
mov ecx, 11
rep movsb
mov word [edi], 20h ; attributes
sub edi, 11
pop esi ecx
add esp, 12
mov byte [edi+13], 0 ; tenths of a second at file creation time
call get_time_for_file
mov [edi+14], ax ; creation time
mov [edi+22], ax ; last write time
call get_date_for_file
mov [edi+16], ax ; creation date
mov [edi+24], ax ; last write date
mov [edi+18], ax ; last access date
and word [edi+20], 0 ; high word of cluster
and word [edi+26], 0 ; low word of cluster - to be filled
and dword [edi+28], 0 ; file size - to be filled
cmp byte [esp+20+28], 0
jz .doit
; create directory
mov byte [edi+11], 10h ; attributes: folder
mov ecx, 32*2
mov edx, edi
push edx
push ecx
push edi
add edi, 26 ; edi points to low word of cluster
push edi
mov edi, [esp+16+20+20]
jmp .doit2
.doit:
push edx
push ecx
push edi
add edi, 26 ; edi points to low word of cluster
push edi
jecxz .done
mov ecx, 2849
mov edi, RAMDISK_FAT
.write_loop:
; allocate new cluster
xor eax, eax
repnz scasw
jnz .disk_full2
.doit2:
dec edi
dec edi
 
; lea eax, [edi-(RAMDISK_FAT)]
 
mov eax, edi
sub eax, RAMDISK_FAT
 
shr eax, 1 ; eax = cluster
mov word [edi], 0xFFF ; mark as last cluster
xchg edi, [esp]
stosw
pop edi
push edi
inc ecx
; write data
cmp byte [esp+16+20+28], 0
jnz .writedir
shl eax, 9
add eax, RAMDISK+31*512
.writefile:
mov ebx, edx
xchg eax, ebx
push ecx
mov ecx, 512
cmp dword [esp+12], ecx
jae @f
mov ecx, [esp+12]
@@:
call memmove
add edx, ecx
sub [esp+12], ecx
pop ecx
jnz .write_loop
.done:
mov ebx, edx
pop edi edi ecx edx
sub ebx, edx
mov [edi+28], ebx
add esp, 20
mov [esp+16], ebx
popad
xor eax, eax
ret
.disk_full2:
mov ebx, edx
pop edi edi ecx edx
sub ebx, edx
mov [edi+28], ebx
add esp, 20
mov [esp+16], ebx
popad
movi eax, ERROR_DISK_FULL
ret
.writedir:
mov edi, eax
; 3. Store the actual number of sectors.
mov [edi], ecx
; 4. Calculate source and destination addresses.
if operation = 0 ; reading?
mov esi, dword [start_sector]
shl esi, 9
add esi, RAMDISK
mov edi, [buffer]
else ; writing?
mov edi, dword [start_sector]
shl edi, 9
add edi, RAMDISK+31*512
mov esi, edx
mov ecx, 32/4
push ecx
add edi, RAMDISK
mov esi, [buffer]
end if
; 5. Calculate number of dwords to be transferred.
shl ecx, 9-2
; 6. Copy data.
rep movsd
mov dword [edi-32], '. '
mov dword [edi-32+4], ' '
mov dword [edi-32+8], ' '
mov byte [edi-32+11], 10h
mov word [edi-32+26], ax
mov esi, edx
pop ecx
rep movsd
mov dword [edi-32], '.. '
mov dword [edi-32+4], ' '
mov dword [edi-32+8], ' '
mov byte [edi-32+11], 10h
mov eax, [esp+16+8]
mov word [edi-32+26], ax
xor eax, eax
mov ecx, (512-32*2)/4
rep stosd
pop edi edi ecx edx
add esp, 20
popad
xor eax, eax
xor ebx, ebx
ret
; 7. Return. The value in eax was calculated in step 2.
pop edi esi ; restore used registers to be stdcall
}
 
.read_symbol:
or ax, -1
test esi, esi
jz .retFFFF
lodsb
test al, al
jnz ansi2uni_char
xor eax, eax
xor esi, esi
.retFFFF:
; Reads one or more sectors from the device.
proc ramdisk_read userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword
ramdisk_read_write 0
ret
endp
 
.read_symbols:
call .read_symbol
stosw
loop .read_symbols
; Writes one or more sectors to the device.
proc ramdisk_write userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword
ramdisk_read_write 1
ret
endp
 
;----------------------------------------------------------------
;
; fs_RamdiskWrite - LFN variant for writing to sys floppy
;
; esi points to filename
; ebx pointer to 64-bit number = first wanted byte, 0+
; may be ebx=0 - start from first byte
; ecx number of bytes to write, 0+
; edx mem location to data
;
; ret ebx = bytes written (maybe 0)
; eax = 0 ok write or other = errormsg
;
;--------------------------------------------------------------
@@:
push ERROR_ACCESS_DENIED
fs_RamdiskWrite.ret0:
pop eax
xor ebx, ebx
ret
 
fs_RamdiskWrite:
cmp byte [esi], 0
jz @b
pushad
call rd_find_lfn
jnc .found
popad
push ERROR_FILE_NOT_FOUND
jmp .ret0
.found:
; must not be directory
test byte [edi+11], 10h
jz @f
popad
push ERROR_ACCESS_DENIED
jmp .ret0
@@:
; FAT does not support files larger than 4GB
test ebx, ebx
jz .l1
cmp dword [ebx+4], 0
jz @f
.eof:
popad
push ERROR_END_OF_FILE
jmp .ret0
@@:
mov ebx, [ebx]
.l1:
; now edi points to direntry, ebx=start byte to write,
; ecx=number of bytes to write, edx=data pointer
call fat_update_datetime
 
; extend file if needed
add ecx, ebx
jc .eof ; FAT does not support files larger than 4GB
push 0 ; return value=0
cmp ecx, [edi+28]
jbe .length_ok
cmp ecx, ebx
jz .length_ok
call ramdisk_extend_file
jnc .length_ok
; ramdisk_extend_file can return two error codes: FAT table error or disk full.
; First case is fatal error, in second case we may write some data
mov [esp], eax
cmp al, ERROR_DISK_FULL
jz .disk_full
pop eax
mov [esp+28], eax
popad
xor ebx, ebx
ret
.disk_full:
; correct number of bytes to write
mov ecx, [edi+28]
cmp ecx, ebx
ja .length_ok
.ret:
pop eax
mov [esp+28], eax ; eax=return value
sub edx, [esp+20]
mov [esp+16], edx ; ebx=number of written bytes
popad
ret
.length_ok:
; now ebx=start pos, ecx=end pos, both lie inside file
sub ecx, ebx
jz .ret
movzx edi, word [edi+26] ; starting cluster
.write_loop:
sub ebx, 0x200
jae .next_cluster
push ecx
neg ebx
cmp ecx, ebx
jbe @f
mov ecx, ebx
@@:
mov eax, edi
shl eax, 9
add eax, RAMDISK+31*512+0x200
sub eax, ebx
mov ebx, eax
mov eax, edx
call memmove
xor ebx, ebx
add edx, ecx
sub [esp], ecx
pop ecx
jz .ret
.next_cluster:
movzx edi, word [edi*2+RAMDISK_FAT]
jmp .write_loop
 
ramdisk_extend_file.zero_size:
; The kernel calls this function when initializing cache subsystem for
; the media. This call allows the driver to adjust the cache size.
proc ramdisk_adjust_cache_size
virtual at esp+4
.userdata dd ?
.suggested_size dd ?
end virtual
; Since ramdisk does not need cache, just return 0.
xor eax, eax
jmp ramdisk_extend_file.start_extend
 
; extends file on ramdisk to given size, new data area is filled by 0
; in: edi->direntry, ecx=new size
; out: CF=0 => OK, eax=0
; CF=1 => error, eax=code (ERROR_FAT_TABLE or ERROR_DISK_FULL)
ramdisk_extend_file:
push ecx
; find the last cluster of file
movzx eax, word [edi+26] ; first cluster
mov ecx, [edi+28]
jecxz .zero_size
@@:
sub ecx, 0x200
jbe @f
mov eax, [eax*2+RAMDISK_FAT]
and eax, 0xFFF
jz .fat_err
cmp eax, 0xFF8
jb @b
.fat_err:
pop ecx
movi eax, ERROR_FAT_TABLE
stc
ret
@@:
push eax
mov eax, [eax*2+RAMDISK_FAT]
and eax, 0xFFF
cmp eax, 0xFF8
pop eax
jb .fat_err
; set length to full number of sectors and make sure that last sector is zero-padded
sub [edi+28], ecx
push eax edi
mov edi, eax
shl edi, 9
lea edi, [edi+RAMDISK+31*512+0x200+ecx]
neg ecx
xor eax, eax
rep stosb
pop edi eax
.start_extend:
pop ecx
; now do extend
push edx esi
mov esi, RAMDISK_FAT+2*2 ; start scan from cluster 2
mov edx, 2847 ; number of clusters to scan
.extend_loop:
cmp [edi+28], ecx
jae .extend_done
; add new sector
push ecx
mov ecx, edx
push edi
mov edi, esi
jecxz .disk_full
push eax
xor eax, eax
repnz scasw
pop eax
jnz .disk_full
mov word [edi-2], 0xFFF
mov esi, edi
mov edx, ecx
sub edi, RAMDISK_FAT
shr edi, 1
dec edi ; now edi=new cluster
test eax, eax
jz .first_cluster
mov [RAMDISK_FAT+eax*2], di
jmp @f
.first_cluster:
pop eax ; eax->direntry
push eax
mov [eax+26], di
@@:
push edi
shl edi, 9
add edi, RAMDISK+31*512
xor eax, eax
mov ecx, 512/4
rep stosd
pop eax ; eax=new cluster
pop edi ; edi->direntry
pop ecx ; ecx=required size
add dword [edi+28], 0x200
jmp .extend_loop
.extend_done:
mov [edi+28], ecx
pop esi edx
xor eax, eax ; CF=0
ret
.disk_full:
pop edi ecx
pop esi edx
stc
movi eax, ERROR_DISK_FULL
ret
 
fat_update_datetime:
call get_time_for_file
mov [edi+22], ax ; last write time
call get_date_for_file
mov [edi+24], ax ; last write date
mov [edi+18], ax ; last access date
ret
 
;----------------------------------------------------------------
;
; fs_RamdiskSetFileEnd - set end of file on ramdisk
;
; esi points to filename
; ebx points to 64-bit number = new file size
; ecx ignored (reserved)
; edx ignored (reserved)
;
; ret eax = 0 ok or other = errormsg
;
;--------------------------------------------------------------
fs_RamdiskSetFileEnd:
cmp byte [esi], 0
jnz @f
.access_denied:
push ERROR_ACCESS_DENIED
jmp .ret
@@:
push edi
call rd_find_lfn
jnc @f
pop edi
push ERROR_FILE_NOT_FOUND
.ret:
pop eax
ret
@@:
; must not be directory
test byte [edi+11], 10h
jz @f
pop edi
jmp .access_denied
@@:
; file size must not exceed 4Gb
cmp dword [ebx+4], 0
jz @f
pop edi
push ERROR_END_OF_FILE
jmp .ret
@@:
; set file modification date/time to current
call fat_update_datetime
mov eax, [ebx]
cmp eax, [edi+28]
jb .truncate
ja .expand
pop edi
xor eax, eax
ret
.expand:
push ecx
mov ecx, eax
call ramdisk_extend_file
pop ecx
pop edi
ret
.truncate:
mov [edi+28], eax
push ecx
movzx ecx, word [edi+26]
test eax, eax
jz .zero_size
; find new last sector
@@:
sub eax, 0x200
jbe @f
movzx ecx, word [RAMDISK_FAT+ecx*2]
jmp @b
@@:
; zero data at the end of last sector
push ecx
mov edi, ecx
shl edi, 9
lea edi, [edi+RAMDISK+31*512+eax+0x200]
mov ecx, eax
neg ecx
xor eax, eax
rep stosb
pop ecx
; terminate FAT chain
lea ecx, [RAMDISK_FAT+ecx+ecx]
push dword [ecx]
mov word [ecx], 0xFFF
pop ecx
and ecx, 0xFFF
jmp .delete
.zero_size:
and word [edi+26], 0
.delete:
; delete FAT chain starting with ecx
; mark all clusters as free
cmp ecx, 0xFF8
jae .deleted
lea ecx, [RAMDISK_FAT+ecx+ecx]
push dword [ecx]
and word [ecx], 0
pop ecx
and ecx, 0xFFF
jmp .delete
.deleted:
pop ecx
pop edi
xor eax, eax
ret
 
fs_RamdiskGetFileInfo:
cmp byte [esi], 0
jnz @f
mov eax, 2 ; unsupported
ret
@@:
push edi
call rd_find_lfn
fs_GetFileInfo_finish:
jnc @f
pop edi
mov eax, ERROR_FILE_NOT_FOUND
ret
@@:
push esi ebp
xor ebp, ebp
mov esi, edx
and dword [esi+4], 0
call fat_entry_to_bdfe2
pop ebp esi
pop edi
xor eax, eax
ret
 
fs_RamdiskSetFileInfo:
cmp byte [esi], 0
jnz @f
mov eax, 2 ; unsupported
ret
@@:
push edi
call rd_find_lfn
jnc @f
pop edi
mov eax, ERROR_FILE_NOT_FOUND
ret
@@:
call bdfe_to_fat_entry
pop edi
xor eax, eax
ret
 
;----------------------------------------------------------------
;
; fs_RamdiskDelete - delete file or empty folder from ramdisk
;
; esi points to filename
;
; ret eax = 0 ok or other = errormsg
;
;--------------------------------------------------------------
fs_RamdiskDelete:
cmp byte [esi], 0
jnz @f
; cannot delete root!
.access_denied:
push ERROR_ACCESS_DENIED
.pop_ret:
pop eax
ret
@@:
and [rd_prev_sector], 0
and [rd_prev_prev_sector], 0
push edi
call rd_find_lfn
jnc .found
pop edi
push ERROR_FILE_NOT_FOUND
jmp .pop_ret
.found:
cmp dword [edi], '. '
jz .access_denied2
cmp dword [edi], '.. '
jz .access_denied2
test byte [edi+11], 10h
jz .dodel
; we can delete only empty folders!
movzx eax, word [edi+26]
push ebx
mov ebx, eax
shl ebx, 9
add ebx, RAMDISK + 31*0x200 + 2*0x20
.checkempty:
cmp byte [ebx], 0
jz .empty
cmp byte [ebx], 0xE5
jnz .notempty
add ebx, 0x20
test ebx, 0x1FF
jnz .checkempty
movzx eax, word [RAMDISK_FAT + eax*2]
test eax, eax
jz .empty
mov ebx, eax
shl ebx, 9
add ebx, RAMDISK + 31*0x200
jmp .checkempty
.notempty:
pop ebx
.access_denied2:
pop edi
jmp .access_denied
.empty:
pop ebx
.dodel:
movzx eax, word [edi+26]
; delete folder entry
mov byte [edi], 0xE5
; delete LFN (if present)
.lfndel:
test edi, 0x1FF
jnz @f
cmp [rd_prev_sector], 0
jz @f
cmp [rd_prev_sector], -1
jz .lfndone
mov edi, [rd_prev_sector]
push [rd_prev_prev_sector]
pop [rd_prev_sector]
or [rd_prev_prev_sector], -1
shl edi, 9
add edi, RAMDISK + 31*0x200 + 0x200
@@:
sub edi, 0x20
cmp byte [edi], 0xE5
jz .lfndone
cmp byte [edi+11], 0xF
jnz .lfndone
mov byte [edi], 0xE5
jmp .lfndel
.lfndone:
; delete FAT chain
cmp eax, 2
jb .done
cmp eax, 0xFF8
jae .done
lea eax, [RAMDISK_FAT + eax*2]
push dword [eax]
and word [eax], 0
pop eax
and eax, 0xFFF
jmp .lfndone
.done:
pop edi
xor eax, eax
ret
 
; \end{diamond}
retn 8
endp
/kernel/branches/Kolibri-acpi/blkdev/rdsave.inc
13,7 → 13,8
dd 2 ; subfunction: write
dd 0 ; (reserved)
dd 0 ; (reserved)
dd 1440*1024 ; size 1440 Kb
.size:
dd 0
dd RAMDISK
db 0
.name:
20,9 → 21,11
dd ?
endg
sysfn_saveramdisk: ; 18.6 = SAVE FLOPPY IMAGE (HD version only)
call restorefatchain
mov ebx, saverd_fileinfo
mov [saverd_fileinfo.name], ecx
mov [ebx+21], ecx
mov eax, [ramdisk_actual_size]
shl eax, 9
mov [ebx+12], eax
pushad
call file_system_lfn_protected ;in ebx
popad
/kernel/branches/Kolibri-acpi/boot/booteng.inc
File deleted
Property changes:
Deleted: svn:keywords
-Rev
\ No newline at end of property
/kernel/branches/Kolibri-acpi/boot/boot_fat12.asm
File deleted
/kernel/branches/Kolibri-acpi/core/sys32.inc
660,10 → 660,6
call free_cd_channel
and [cd_status], 0
@@:
cmp [flp_status], esi
jnz @f
and [flp_status], 0
@@:
pop esi
cmp [bgrlockpid], esi
jnz @f
/kernel/branches/Kolibri-acpi/core/syscall.inc
7,20 → 7,6
 
$Revision$
 
; Old style system call converter
align 16
cross_order:
; load all registers in crossed order
mov eax, ebx
mov ebx, ecx
mov ecx, edx
mov edx, esi
mov esi, edi
movzx edi, byte[esp+28 + 4]
sub edi, 53
call dword [servetable+edi*4]
ret
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; SYSENTER ENTRY ;;
109,25 → 95,7
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SYSTEM FUNCTIONS TABLE ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
align 4
servetable:
dd 0
dd 0
dd 0
dd 0
dd 0
dd file_system ; 58-Common file system interface
dd 0
dd 0
dd 0
dd 0
dd 0
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; NEW SYSTEM FUNCTIONS TABLE ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
servetable2:
 
dd syscall_draw_window ; 0-DrawWindow
136,7 → 104,7
dd sys_clock ; 3-GetTime
dd syscall_writetext ; 4-WriteText
dd delay_hs_unprotected ; 5-DelayHs
dd syscall_openramdiskfile ; 6-OpenRamdiskFile
dd undefined_syscall ; 6-deprecated OpenRamdiskFile
dd syscall_putimage ; 7-PutImage
dd syscall_button ; 8-DefineButton
dd sys_cpuusage ; 9-GetProcessInfo
188,7 → 156,7
dd sound_interface ; 55-Sound interface
dd undefined_syscall ; 56-reserved
dd sys_pcibios ; 57-PCI BIOS32
dd cross_order ; 58-Common file system interface
dd undefined_syscall ; 58-deprecated Common file system interface
dd undefined_syscall ; 59-reserved
dd sys_IPC ; 60-Inter Process Communication
dd sys_gs ; 61-Direct graphics access
/kernel/branches/Kolibri-acpi/data32.inc
51,7 → 51,7
if lang eq ru
boot_initirq cp866 'Инициализация IRQ',0
boot_picinit cp866 'Инициализация PIC',0
boot_v86machine cp866 'Инициализация системы V86 машины',0
boot_v86machine cp866 'Инициализация системной V86 машины',0
boot_inittimer cp866 'Инициализация системного таймера (IRQ0)',0
boot_initapic cp866 'Попытка инициализации APIC',0
boot_enableirq cp866 'Включить прерывания 2, 13',0
69,6 → 69,7
boot_cpuid cp866 'Чтение CPUIDs',0
; boot_devices cp866 'Поиск устройств',0
boot_timer cp866 'Установка таймера',0
boot_initramdisk cp866 'Инициализация рамдиска',0
boot_irqs cp866 'Переопределение IRQ',0
boot_setmouse cp866 'Установка мыши',0
boot_windefs cp866 'Установка настроек окон по умолчанию',0
98,6 → 99,7
boot_picinit db 'Initialize PIC',0
boot_v86machine db 'Initialize system V86 machine',0
boot_inittimer db 'Initialize system timer (IRQ0)',0
boot_initramdisk db 'Initialize ramdisk',0
boot_initapic db 'Try to initialize APIC',0
boot_enableirq db 'Enable interrupts 2, 13',0
boot_disabling_ide db 'Disable interrupts in IDE controller',0
172,7 → 174,13
 
vmode db '/sys/drivers/VMODE.MDR',0
;vrr_m db 'VRR_M',0
kernel_file db 'KERNEL MNT'
kernel_file_load:
; load kernel.mnt to 0x7000:0
dd 0 ; subfunction
dq 0 ; offset in file
dd 0x30000 ; number of bytes to read
dd OS_BASE + 0x70000 ; buffer for data
db '/RD/1/KERNEL.MNT',0
 
dev_data_path db '/RD/1/DRIVERS/DEVICES.DAT',0
 
570,8 → 578,7
org (OS_BASE+0x0100000)
 
RAMDISK: rb 2880*512
RAMDISK_FAT: rb 2856*2
FLOPPY_FAT: rb 2856*2
rb 2856*4 ; not used
 
_CLEAN_ZONE:
 
/kernel/branches/Kolibri-acpi/data32et.inc
2,6 → 2,7
boot_picinit latin1 'Algväärtustan PIC',0
boot_v86machine latin1 'Algväärtustan süsteemi V86 masinat',0
boot_inittimer latin1 'Algväärtustan süsteemi taimerit (IRQ0)',0
boot_initramdisk latin1 'Initialize ramdisk',0
boot_initapic latin1 'Proovin Algväärtustada APIC',0
boot_enableirq latin1 'Luban katkestused 2, 13',0
boot_disabling_ide latin1 'Keelan IDE kontrolleri katkestused',0
/kernel/branches/Kolibri-acpi/data32sp.inc
2,6 → 2,7
boot_picinit: cp850 'Inicializar PIC',0
boot_v86machine: cp850 'Inicializar sistema V86',0
boot_inittimer: cp850 'Inicializar reloj del sistema (IRQ0)',0
boot_initramdisk cp850 'Initialize ramdisk',0
boot_initapic: cp850 'Prueba inicializar APIC',0
boot_enableirq: cp850 'Habilitar interrupciones 2, 13',0
boot_disabling_ide:cp850 'Habiliar interrupciones en controladores IDE',0
/kernel/branches/Kolibri-acpi/detect/dev_fd.inc
33,5 → 33,6
 
stdcall attach_int_handler, 6, FDCInterrupt, 0
DEBUGF 1, "K : Set IDE IRQ6 return code %x\n", eax
call floppy_init
@@:
 
/kernel/branches/Kolibri-acpi/detect/vortex86.inc
36,6 → 36,13
Vortex86SoCnum = ($ - Vortex86SoClist) / 4 ; Calculate the total number of known Vortex86 CPUs (if id=Vortex86SoCnum+1 --> unknown SoC)
endg
 
; When in debug mode, perform SoC detection regardless of the actual CPU vendor (even for vendors other than DMP)
; When in normal (not debug) mode, check the CPU vendor first, and perform SoC detection only if vendor is 'Vortex86 SoC'
if ~ VORTEX86DEBUG
cmp [cpu_vendor], 'Vort'
jnz .Vortex86end ; If the CPU vendor is not 'Vortex86 SoC', skip the SoC detection
end if
 
mov dx, 0xcf8 ; CF8h = Vortex86 PCI Configuration Address port
mov eax, 0x80000090 ; 0x80000090 = Starting PCI address to read from (32-bit register - accessed as DWORD)
out dx, eax ; Send request to PCI address port to retrieve data from this address
42,8 → 49,7
mov dx, 0xcfc ; CFCh = Vortex86 PCI Configuration Data port
in eax, dx ; Read data (SoC type) from PCI data port
 
if VORTEX86DEBUG
; // Used for debug purposes: testing in emulator and in non-Vortex86 CPU computers
if VORTEX86DEBUG ; When in debug mode, pretend that we received port output equal to "VORTEX86DEBUGVALUE"
mov eax, VORTEX86DEBUGVALUE
end if
55,10 → 61,9
cmp ax, 4d44h ; Check whether it's Vortex86 family (all Vortex86 SoC have ID in form of "0xNN504d44")
jnz .notVortex86
shr eax, 16 ; Discard lower word in EAX which is always 4d44h in Vortex86 family
cmp al, 50h ; The 3rd byte is always 50h in Vortex86 SoC
cmp al, 50h ; The 3rd byte is always 50h in Vortex86 SoC (if this is the case, we need just the highest byte)
jnz .notVortex86
shr ax, 8 ; Discard 3rd byte in EAX, the highest byte determines the SoC type
mov bl, al ; Copy SoC type to BL since EAX (that contains AL) is used implicitly in "LODSD" command below
mov bl, ah ; Copy SoC type to BL since EAX (that includes AH) is used implicitly in "LODSD" command below
mov esi, Vortex86SoClist ; ESI points to the start of Vortex86SoClist (used implicitly in "LODSD" command below)
xor ecx, ecx ; Zero ECX (it is used as counter)
cld ; Clears the DF flag in the EFLAGS register (DF=0 --> String operations increment ESI)
83,7 → 88,7
.unknownVortex86:
mov [Vortex86CPUid], cl ; Save the CPUid (Vortex86SoCnum+1=Unknown Vortex86)
DEBUGF 1, "unknown Vortex86 CPU (has id=%d, last known is %d)\n", [Vortex86CPUid]:1, Vortex86SoCnum
DEBUGF 1, "unknown Vortex86 CPU (id=%d, last known is %d)\n", [Vortex86CPUid]:1, Vortex86SoCnum
jmp .Vortex86end
.notVortex86: ; In case this register is used by other CPUs for other purpose, it's interesting what it contains
/kernel/branches/Kolibri-acpi/drivers/usbhid.asm
File deleted
/kernel/branches/Kolibri-acpi/drivers/usb/urb.inc
File deleted
/kernel/branches/Kolibri-acpi/drivers/usb/usb.asm
File deleted
/kernel/branches/Kolibri-acpi/drivers/usb
Property changes:
Deleted: svn:ignore
-*.mnt
-lang.inc
-*.bat
-out.txt
-scin*
-*.obj
/kernel/branches/Kolibri-acpi/drivers/agp.asm
0,0 → 1,310
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; simple AGP driver for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
format MS COFF
 
DEBUG equ 1
FAST_WRITE equ 0 ; may cause problems with some motherboards
 
include 'proc32.inc'
include 'imports.inc'
 
struc IOCTL
{ .handle dd ?
.io_code dd ?
.input dd ?
.inp_size dd ?
.output dd ?
.out_size dd ?
}
 
virtual at 0
IOCTL IOCTL
end virtual
 
public START
public service_proc
public version
 
DRV_ENTRY equ 1
DRV_EXIT equ -1
 
SRV_GETVERSION equ 0
SRV_DETECT equ 1
 
API_VERSION equ 1
 
section '.flat' code readable align 16
 
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
.entry:
 
if DEBUG
mov esi, msgInit
call SysMsgBoardStr
end if
 
stdcall RegService, my_service, service_proc
ret
.fail:
.exit:
xor eax, eax
ret
endp
 
handle equ IOCTL.handle
io_code equ IOCTL.io_code
input equ IOCTL.input
inp_size equ IOCTL.inp_size
output equ IOCTL.output
out_size equ IOCTL.out_size
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov ebx, [ioctl]
mov eax, [ebx+io_code]
cmp eax, SRV_GETVERSION
jne @F
 
mov eax, [ebx+output]
cmp [ebx+out_size], 4
jne .fail
mov [eax], dword API_VERSION
xor eax, eax
ret
@@:
mov ebx, [ioctl]
mov eax, [ebx+io_code]
cmp eax, SRV_DETECT
jne @F
call detect
@@:
.fail:
or eax, -1
ret
endp
 
restore handle
restore io_code
restore input
restore inp_size
restore output
restore out_size
 
align 4
proc detect
locals
last_bus dd ?
endl
 
mov esi, msgSearch
call SysMsgBoardStr
 
xor eax, eax
mov [bus], eax
inc eax
call PciApi ; get last bus
cmp eax, -1
je .error
mov [last_bus], eax
 
.next_bus:
and [devfn], 0
.next_dev:
stdcall PciRead16, [bus], [devfn], dword 0x0a ; read class/subclass
 
cmp ax, 0x0300 ; display controller - vga compatable controller
je .found
 
cmp ax, 0x0302 ; display controller - 3d controller
je .found
 
cmp ax, 0x0380 ; display controller - other display controller
je .found
 
.next:
inc [devfn]
cmp [devfn], 256
jb .next_dev
mov eax, [bus]
inc eax
mov [bus], eax
cmp eax, [last_bus]
jna .next_bus
 
.error:
mov esi, msgFail
call SysMsgBoardStr
 
xor eax, eax
inc eax
ret
 
.found:
stdcall PciRead8, [bus], [devfn], dword 0x06 ; read prog IF
test al, 1 shl 4 ; got capabilities list?
jnz .got_capabilities_list
 
; TODO: Do it the old way: detect device and check with a list of known capabilities
; stupid pre PCI 2.2 board....
 
jmp .next
 
.got_capabilities_list:
stdcall PciRead8, [bus], [devfn], dword 0x34 ; read capabilities offset
and eax, 11111100b ; always dword aligned
mov edi, eax
 
.read_capability:
stdcall PciRead32, [bus], [devfn], edi ; read capability
cmp al, 0x02 ; AGP
je .got_agp
movzx edi, ah ; pointer to next capability
test edi, edi
jnz .read_capability
jmp .next
 
.got_agp:
shr eax, 16
mov [revision], al ; high nibble = major revision
; low nibble = minor revision
add edi, 4
and al, 0xf0
cmp al, 0x30
je .agp_3
 
.agp_2:
mov esi, msgAGP2
call SysMsgBoardStr
 
stdcall PciRead32, [bus], [devfn], edi ; read AGP status
.agp_2_:
test al, 100b
jnz .100b
 
test al, 10b
jnz .010b
 
test al, 1b
jz .error
 
.001b:
mov [cmd], 001b
mov esi, msg1
call SysMsgBoardStr
jmp .agp_go
 
.010b:
mov [cmd], 010b
mov esi, msg2
call SysMsgBoardStr
jmp .agp_go
 
.100b:
mov [cmd], 100b
mov esi, msg4
call SysMsgBoardStr
jmp .agp_go
 
.agp_2m:
mov esi, msgAGP2m
call SysMsgBoardStr
jmp .agp_2_
 
.agp_3:
mov esi, msgAGP3
call SysMsgBoardStr
 
stdcall PciRead32, [bus], [devfn], edi ; read AGP status
test al, 1 shl 3
jz .agp_2m
 
test eax, 10b
jnz .8x
mov [cmd], 01b
mov esi, msg4
call SysMsgBoardStr
jmp .agp_go
 
.8x:
mov [cmd], 10b
mov esi, msg8
call SysMsgBoardStr
 
.agp_go:
 
if FAST_WRITE
test ax, 1 shl 4
jz @f
or [cmd], 1 shl 4
mov esi, msgfast
call SysMsgBoardStr
@@:
end if
 
test ax, 1 shl 9 ; Side band addressing
jz @f
or [cmd], 1 shl 9
mov esi, msgside
call SysMsgBoardStr
@@:
 
add edi, 4
mov eax, [cmd]
or eax, 1 shl 8 ; enable AGP
stdcall PciWrite32, [bus], [devfn], edi, eax ; write AGP cmd
 
mov esi, msgOK
call SysMsgBoardStr
 
ret
 
endp
 
 
; initialized data
 
align 4
version dd (5 shl 16) or (API_VERSION and 0xFFFF)
 
my_service db 'AGP', 0 ; max 16 chars include zero
 
msgInit db 'AGP driver loaded.', 13, 10, 0
msgSearch db 'Searching for AGP card...', 13, 10, 0
msgFail db 'device not found', 13, 10, 0
msgOK db 'AGP device enabled', 13, 10, 0
msgAGP2 db 'AGP2 device found', 13, 10, 0
msgAGP3 db 'AGP3 device found', 13, 10, 0
msgAGP2m db 'Running in AGP2 mode', 13, 10, 0
msg8 db '8x speed', 13, 10, 0
msg4 db '4x speed', 13, 10, 0
msg2 db '2x speed', 13, 10, 0
msg1 db '1x speed', 13, 10, 0
msgfast db 'Fast Write', 13, 10, 0
msgside db 'Side band addressing', 13, 10, 0
 
section '.data' data readable writable align 16
 
; uninitialized data
 
revision db ?
cmd dd ?
bus dd ?
devfn dd ?
 
/kernel/branches/Kolibri-acpi/drivers/apm.asm
0,0 → 1,350
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2009-2011. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; 11.09.2009 staper@inbox.ru
; see kernel\docs\apm.txt
 
use32
 
org 0x0
 
db 'MENUET01'
dd 0x1
dd START
dd I_END
dd (I_END+100) and not 3
dd (I_END+100) and not 3
dd 0x0,0x0
 
include 'macros.inc'
 
START:
mcall 40,0x7
 
mcall 49,0x0001,0x0001,0x5308 ;CX = FFFFh APM v1.0
; mcall 49,0x0001,0x0001,0x530d
; mcall 49,0x0001,0x0001,0x530f
 
; mcall 49,0x0000,,0x5310 ;bl - number of batteries
redraw:
mcall 49,0x0000,,0x530c
dec cl
jz still
mcall 49,0x0001,0x0001,0x5308
mcall 49,0x01ff,,0x530c
test cl, cl
jz @f
mcall 49,0x0000,0x0001,0x530d
mcall 49,0x0000,0x0000,0x5307
mcall 49,0x0000,0x0001,0x5308
@@:
mcall 12,1
mcall 0,100*65536+235,100*65536+90,0x34ffffff,0x000000,title
mcall 49,0x0000,,0x5300
jnc @f
mcall 4,10*65536+3,0x80000000,text.4
bts [flags], 1
jmp .end
@@:
cmp al, 0
jne @f
mov edx, text.1
jmp .0
@@:
cmp al, 1
jne @f
mov edx, text.2
jmp .0
@@:
mov edx, text.3
.0:
push edx
mcall 4,169*65536+3,0x80dddddd,text.0
pop edx
add ebx, 47*65536
mcall
mcall 49,0x0001,,0x530a
jc .error
push si dx cx bx ;time of battery life, b. flag, b. status, AC line status
 
;AC line status
cmp bh, 0
jne @f
mov edx, text.01
jmp .1
@@:
cmp bh, 1
jne @f
mov edx, text.02
jmp .1
@@:
cmp bh, 2
jne @f
mov edx, text.03
jmp .1
@@:
mov edx, text.04
.1:
push edx
mcall 4,10*65536+10,0x80000000,text.00
pop edx
mcall ,100*65536+10,;0x80000000
 
;battery status
pop bx
cmp bl, 0
jne @f
mov edx, text.11
jmp .2
@@:
cmp bl, 1
jne @f
mov edx, text.12
jmp .2
@@:
cmp bl, 2
jne @f
mov edx, text.13
jmp .2
@@:
cmp bl, 3
jne @f
mov edx, text.14
jmp .2
@@:
mov edx, text.04
.2:
push edx
mcall 4,10*65536+20,0x80000000,text.10
pop edx
mcall ,100*65536+20,
 
;battery life, percentage and minutes/seconds
mcall ,10*65536+30,,text.20
pop cx
cmp cl, 0xff
jne @f
mcall ,100*65536+30,0x80000000,text.04
pop eax
jmp .end
@@:
shl ecx, 24
shr ecx, 24
mcall 47,0x80030000,,100*65536+30,0x347636
.3:
mcall 4,115*65536+30,0x80000000,text.15
mov dx, [esp]
shl edx, 17
shr edx, 17
mov ecx, edx
mcall 47,0x80030000,,140*65536+30
pop cx
mov edx, text.21
bt cx, 15
jc @f
mov edx, text.22
@@:
mcall 4,160*65536+30,0x80000000
pop si
.error:
.end:
;buttons
mcall 8,148*65536+16,45*65536+15,3,0x00677ab0
mcall ,166*65536+16,,4,
mcall ,184*65536+16,,5,
mcall ,202*65536+16,,6,
bt [flags], 1
jc @f
mcall ,65*65536+45,,2,
@@:
mcall 4,10*65536+50,0x80564242,text.30
mcall 12,2
 
still:
; mcall 10
mcall 23,12000
test eax, eax
jz redraw
 
dec al
jz redraw
dec al
jz key
dec al
jz button
jmp still
 
 
 
 
key:
mcall 2
jmp still
 
button:
mcall 17
cmp ah, 1
jne @f
mcall -1
 
@@:
cmp ah, 2
jne @f
mcall 5,50
mcall 49,0x0001,0x0001,0x5307
jmp redraw
 
@@:
cmp ah, 4
jg @f
mov edx, 0x01f7 ;primary chan.
call reserv_ports
jc redraw
sub bh, 3
.1:
call set_drive
btc [flags], 2
jnc .2
call device_reset
jmp .3
.2:
call standby_hdd
.3:
call free_ports
jmp redraw
 
@@:
cmp ah, 6
jg redraw
mov edx, 0x0177 ;secondary chan.
call reserv_ports
jc redraw
sub bh, 5
jmp .1
 
set_drive:
dec dx
in al, dx
test bh, bh
jnz @f
btr ax, 4
.1:
out dx, al
inc dx
ret
@@:
bts ax, 4
jmp .1
 
 
standby_hdd:
; 94h E0h nondata standby immediate
; 95h E1h nondata idle immediate
; 96h E2h nondata standby
; 97h E3h nondata idle
; 98h E5h nondata check power mode
; 99h E6h nondata set sleep mode
xor ecx, ecx
@@:
in al, dx
dec cx
jz @f
bt ax, 6
jnc @b
mov al, 0x96
out dx, al
mov al, 0xe2
out dx, al
@@:
ret
 
reserv_ports:
mov ecx, edx
dec ecx
push ax
mcall 46,0
test al, al
jnz @f
pop bx
clc
ret
@@:
pop bx
stc
ret
 
device_reset:
xor ecx, ecx
@@:
in al, dx
dec cx
jz @f
bt ax, 6
jnc @b
mov al, 0x10
out dx, al
@@:
ret
 
free_ports:
mov ecx, edx
dec ecx
mcall 46,1
ret
 
 
; ДАННЫЕ ПРОГРАММЫ
title db '',0
flags dw 0
 
text:
.0:
db 'APM v.1.',0
.1:
db '0',0
.2:
db '1',0
.3:
db '2',0
.4:
db 'APM not supported',0
 
.00:
db 'power status:',0
.01:
db 'off-line',0
.02:
db 'on-line',0
.03:
db 'on backup power',0
.04:
db 'unknown',0
 
.10:
db 'battery flag:',0
.11:
db 'high',0
.12:
db 'low',0
.13:
db 'critical',0
.14:
db 'charging',0
.15:
db ' % ,',0
 
.20:
db 'battery life:',0
.21:
db 'min',0
.22:
db 'sec',0
 
.30:
db 'STAND-BY: SYSTEM HDD: 0 1 2 3',0
 
I_END:
/kernel/branches/Kolibri-acpi/drivers/fdo.inc
0,0 → 1,453
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
_esp equ esp
 
;
; Formatted Debug Output (FDO)
; Copyright (c) 2005-2006, mike.dld
; Created: 2005-01-29, Changed: 2006-11-10
;
; For questions and bug reports, mail to mike.dld@gmail.com
;
; Available format specifiers are: %s, %d, %u, %x (with partial width support)
;
 
; to be defined:
; __DEBUG__ equ 1
; __DEBUG_LEVEL__ equ 5
 
; MOV Immediate.
; Useful for things like movi eax,10:
; shorter than regular mov, but slightly slower,
; do not use it in performance-critical places.
macro movi dst, imm
{
if imm >= -0x80 & imm <= 0x7F
push imm
pop dst
else
mov dst, imm
end if
}
 
macro debug_func name {
if used name
name@of@func equ name
}
 
macro debug_beginf {
align 4
name@of@func:
}
 
debug_endf fix end if
 
macro DEBUGS _sign,[_str] {
common
local tp
tp equ 0
match _arg:_num,_str \{
DEBUGS_N _sign,_num,_arg
tp equ 1
\}
match =0 _arg,tp _str \{
DEBUGS_N _sign,,_arg
\}
}
 
macro DEBUGS_N _sign,_num,[_str] {
common
pushf
pushad
local ..str,..label,is_str
is_str = 0
forward
if _str eqtype ''
is_str = 1
end if
common
if is_str = 1
jmp ..label
..str db _str,0
..label:
mov edx, ..str
else
esp equ esp+4*8+4
mov edx, _str
esp equ _esp
end if
if ~_num eq
if _num eqtype eax
if _num in <eax,ebx,ecx,edx,edi,ebp,esp>
mov esi, _num
else if ~_num eq esi
movzx esi, _num
end if
else if _num eqtype 0
mov esi, _num
else
local tp
tp equ 0
match [_arg],_num \{
mov esi, dword[_arg]
tp equ 1
\}
match =0 =dword[_arg],tp _num \{
mov esi, dword[_arg]
tp equ 1
\}
match =0 =word[_arg],tp _num \{
movzx esi, word[_arg]
tp equ 1
\}
match =0 =byte[_arg],tp _num \{
movzx esi, byte[_arg]
tp equ 1
\}
match =0,tp \{
'Error: specified string width is incorrect'
\}
end if
else
mov esi, 0x7FFFFFFF
end if
call fdo_debug_outstr
popad
popf
}
 
macro DEBUGD _sign,_dec {
local tp
tp equ 0
match _arg:_num,_dec \{
DEBUGD_N _sign,_num,_arg
tp equ 1
\}
match =0 _arg,tp _dec \{
DEBUGD_N _sign,,_arg
\}
}
 
macro DEBUGD_N _sign,_num,_dec {
pushf
pushad
if (~_num eq)
if (_dec eqtype eax | _dec eqtype 0)
'Error: precision allowed only for in-memory variables'
end if
if (~_num in <1,2,4>)
if _sign
'Error: 1, 2 and 4 are only allowed for precision in %d'
else
'Error: 1, 2 and 4 are only allowed for precision in %u'
end if
end if
end if
if _dec eqtype eax
if _dec in <ebx,ecx,edx,esi,edi,ebp,esp>
mov eax, _dec
else if ~_dec eq eax
if _sign = 1
movsx eax, _dec
else
movzx eax, _dec
end if
end if
else if _dec eqtype 0
mov eax, _dec
else
esp equ esp+4*8+4
if _num eq
mov eax, dword _dec
else if _num = 1
if _sign = 1
movsx eax, byte _dec
else
movzx eax, byte _dec
end if
else if _num = 2
if _sign = 1
movsx eax, word _dec
else
movzx eax, word _dec
end if
else
mov eax, dword _dec
end if
esp equ _esp
end if
mov cl, _sign
call fdo_debug_outdec
popad
popf
}
 
macro DEBUGH _sign,_hex {
local tp
tp equ 0
match _arg:_num,_hex \{
DEBUGH_N _sign,_num,_arg
tp equ 1
\}
match =0 _arg,tp _hex \{
DEBUGH_N _sign,,_arg
\}
}
 
macro DEBUGH_N _sign,_num,_hex {
pushf
pushad
if (~_num eq) & (~_num in <1,2,3,4,5,6,7,8>)
'Error: 1..8 are only allowed for precision in %x'
end if
if _hex eqtype eax
if _hex in <eax,ebx,ecx,edx,esi,edi,ebp,esp>
if ~_hex eq eax
mov eax, _hex
end if
mov edx, 8
else if _hex in <ax,bx,cx,dx,si,di,bp,sp>
if ~_hex eq ax
movzx eax, _hex
end if
if (_num eq)
mov edx, 4
end if
else if _hex in <al,ah,bl,bh,cl,ch,dl,dh>
if ~_hex eq al
movzx eax, _hex
end if
if (_num eq)
mov edx, 2
end if
end if
else if _hex eqtype 0
mov eax, _hex
else
esp equ esp+4*8+4
mov eax, dword _hex
esp equ _esp
end if
if ~_num eq
mov edx, _num
else
if ~_hex eqtype eax
mov edx, 8
end if
end if
call fdo_debug_outhex
popad
popf
}
 
;-----------------------------------------------------------------------------
 
debug_func fdo_debug_outchar
debug_beginf
pushad
movzx ecx, al
mov ebx, 1
; mov ecx,sys_msg_board
; call ecx ; sys_msg_board
stdcall SysMsgBoard
popad
ret
debug_endf
 
debug_func fdo_debug_outstr
debug_beginf
mov ebx, 1
.l1:
dec esi
js .l2
movzx ecx, byte[edx]
or cl, cl
jz .l2
; mov ecx,sys_msg_board
; call ecx ; sys_msg_board
stdcall SysMsgBoard
inc edx
jmp .l1
.l2:
ret
debug_endf
 
debug_func fdo_debug_outdec
debug_beginf
or cl, cl
jz @f
or eax, eax
jns @f
neg eax
push eax
mov al, '-'
call fdo_debug_outchar
pop eax
@@:
movi ecx, 10
push -'0'
.l1:
xor edx, edx
div ecx
push edx
test eax, eax
jnz .l1
.l2:
pop eax
add al, '0'
jz .l3
call fdo_debug_outchar
jmp .l2
.l3:
ret
debug_endf
 
debug_func fdo_debug_outhex
__fdo_hexdigits db '0123456789ABCDEF'
debug_beginf
mov cl, dl
neg cl
add cl, 8
shl cl, 2
rol eax, cl
.l1:
rol eax, 4
push eax
and eax, 0x0000000F
mov al, [__fdo_hexdigits+eax]
call fdo_debug_outchar
pop eax
dec edx
jnz .l1
ret
debug_endf
 
;-----------------------------------------------------------------------------
 
macro DEBUGF _level,_format,[_arg] {
common
if __DEBUG__ = 1 & _level >= __DEBUG_LEVEL__
local ..f1,f2,a1,a2,c1,c2,c3,..lbl
_debug_str_ equ __debug_str_ # a1
a1 = 0
c2 = 0
c3 = 0
f2 = 0
repeat ..lbl-..f1
virtual at 0
db _format,0,0
load c1 word from %-1
end virtual
if c1 = '%s'
virtual at 0
db _format,0,0
store word 0 at %-1
load c1 from f2-c2
end virtual
if c1 <> 0
DEBUGS 0,_debug_str_+f2-c2
end if
c2 = c2 + 1
f2 = %+1
DEBUGF_HELPER S,a1,0,_arg
else if c1 = '%x'
virtual at 0
db _format,0,0
store word 0 at %-1
load c1 from f2-c2
end virtual
if c1 <> 0
DEBUGS 0,_debug_str_+f2-c2
end if
c2 = c2 + 1
f2 = %+1
DEBUGF_HELPER H,a1,0,_arg
else if c1 = '%d' | c1 = '%u'
local c4
if c1 = '%d'
c4 = 1
else
c4 = 0
end if
virtual at 0
db _format,0,0
store word 0 at %-1
load c1 from f2-c2
end virtual
if c1 <> 0
DEBUGS 0,_debug_str_+f2-c2
end if
c2 = c2 + 1
f2 = %+1
DEBUGF_HELPER D,a1,c4,_arg
else if c1 = '\n'
c3 = c3 + 1
end if
end repeat
virtual at 0
db _format,0,0
load c1 from f2-c2
end virtual
if (c1<>0)&(f2<>..lbl-..f1-1)
DEBUGS 0,_debug_str_+f2-c2
end if
virtual at 0
..f1 db _format,0
..lbl:
__debug_strings equ __debug_strings,_debug_str_,<_format>,..lbl-..f1-1-c2-c3
end virtual
end if
}
 
macro __include_debug_strings dummy,[_id,_fmt,_len] {
common
local c1,a1,a2
forward
if defined _len & ~_len eq
_id:
a1 = 0
a2 = 0
repeat _len
virtual at 0
db _fmt,0,0
load c1 word from %+a2-1
end virtual
if (c1='%s')|(c1='%x')|(c1='%d')|(c1='%u')
db 0
a2 = a2 + 1
else if (c1='\n')
dw $0A0D
a1 = a1 + 1
a2 = a2 + 1
else
db c1 and 0x0FF
end if
end repeat
db 0
end if
}
 
macro DEBUGF_HELPER _letter,_num,_sign,[_arg] {
common
local num
num = 0
forward
if num = _num
DEBUG#_letter _sign,_arg
end if
num = num+1
common
_num = _num+1
}
 
macro include_debug_strings {
if __DEBUG__ = 1
match dbg_str,__debug_strings \{
__include_debug_strings dbg_str
\}
end if
}
/kernel/branches/Kolibri-acpi/drivers/usbhid/keyboard.inc
0,0 → 1,475
; HID keyboard driver, part of USBHID driver.
 
; Global constants.
; They are assembled in a macro to separate code and data;
; the code is located at the point of "include 'keyboard.inc'",
; the data are collected when workers_globals is instantiated.
macro workers_globals
{
; include global constants from previous workers
workers_globals
align 4
; Callbacks for HID layer.
keyboard_driver:
dd keyboard_driver_add_device
dd keyboard_driver_disconnect
dd keyboard_driver_begin_packet
dd keyboard_driver_array_overflow?
dd keyboard_driver_input_field
dd keyboard_driver_end_packet
; Callbacks for keyboard layer.
kbd_functions:
dd 12
dd CloseKeyboard
dd SetKeyboardLights
; Kernel keyboard layer takes input in form of PS/2 scancodes.
; data for keyboard: correspondence between HID usage keys and PS/2 scancodes.
EX = 80h ; if set, precede the scancode with special scancode 0xE0
label control_keys byte
; Usages 700E0h ... 700E7h: LCtrl, LShift, LAlt, LWin, RCtrl, RShift, RAlt, RWin
db 1Dh, 2Ah, 38h, 5Bh+EX, 1Dh+EX, 36h, 38h+EX, 5Ch+EX
; Usages 70004h ... 70004h + normal_keys_number - 1
label normal_keys byte
db 1Eh, 30h, 2Eh, 20h, 12h, 21h, 22h, 23h, 17h, 24h, 25h, 26h, 32h, 31h, 18h, 19h
db 10h, 13h, 1Fh, 14h, 16h, 2Fh, 11h, 2Dh, 15h, 2Ch, 02h, 03h, 04h, 05h, 06h, 07h
db 08h, 09h, 0Ah, 0Bh, 1Ch, 01h, 0Eh, 0Fh, 39h, 0Ch, 0Dh, 1Ah, 1Bh, 2Bh, 0, 27h
db 28h, 29h, 33h, 34h, 35h, 3Ah, 3Bh, 3Ch, 3Dh, 3Eh, 3Fh, 40h, 41h, 42h, 43h, 44h
db 57h, 58h,37h+EX,46h,0,52h+EX,47h+EX,49h+EX,53h+EX,4Fh+EX,51h+EX,4Dh+EX,4Bh+EX,50h+EX,48h+EX,45h
db 35h+EX,37h,4Ah,4Eh,1Ch+EX,4Fh,50h, 51h, 4Bh, 4Ch, 4Dh, 47h, 48h, 49h, 52h, 53h
db 0,5Dh+EX,5Eh+EX
normal_keys_number = $ - normal_keys
}
 
; Data that are specific for one keyboard device.
struct keyboard_device_data
handle dd ? ; keyboard handle from RegKeyboard
timer dd ? ; auto-repeat timer handle
repeatkey db ? ; auto-repeat key code
rb 3 ; padding
usbdev dd ? ; pointer to device_data of USB and HID layers
modifiers dd ? ; state of LCtrl ... RWin
led_report dd ? ; output report for LEDs state
numlock_bit dd ? ; position of NumLock bit in LED output report
capslock_bit dd ?
scrolllock_bit dd ? ; guess what
ends
 
; This procedure is called when HID layer detects a new keyboard.
; in: ebx -> usb_device_data, edi -> collection
; out: eax = device-specific data or NULL on error
proc keyboard_driver_add_device
; 1. Allocate memory for keyboard_device_data. If failed, return NULL.
movi eax, sizeof.keyboard_device_data
call Kmalloc
test eax, eax
jz .nothing
; 2. Initialize keyboard_device_data: store pointer to USB layer data,
; zero some fields, initialize bit positions to -1.
mov [eax+keyboard_device_data.usbdev], ebx
xor ecx, ecx
mov [eax+keyboard_device_data.timer], ecx
mov [eax+keyboard_device_data.repeatkey], cl
mov [eax+keyboard_device_data.modifiers], ecx
mov [eax+keyboard_device_data.led_report], ecx
dec ecx
mov [eax+keyboard_device_data.numlock_bit], ecx
mov [eax+keyboard_device_data.capslock_bit], ecx
mov [eax+keyboard_device_data.scrolllock_bit], ecx
; 3. Look for LED report and bits corresponding to indicators.
; For now, assume that all LEDs are set by the same report.
; 3a. Save registers.
push ebx esi
; 3b. Prepare for loop over output reports: get the first output report.
; If there are no output records, skip step 3;
; default values of led_report and *_bit were set in step 2.
mov edx, [edi+collection.output.first_report]
test edx, edx
jz .led_report_set
.scan_led_report:
; Process one output report.
; 3c. Prepare for loop over field groups in the current report:
; get the first field group.
mov ecx, [edx+report.first_field]
.scan_led_field:
; Process one field group.
; 3d. If there are no more field groups, exit the loop over field groups.
test ecx, ecx
jz .next_led_report
; For now, assume that all LEDs are plain variable fields, not arrays.
; 3e. Ignore array field groups.
test byte [ecx+report_field_group.flags], HID_FIELD_VARIABLE
jz .next_led_field
; 3f. Loop over all fields in the current group.
push [ecx+report_field_group.count]
; esi = pointer to usage of the current field
lea esi, [ecx+report_field_group.common_sizeof]
; ebx = bit position of the current field
mov ebx, [ecx+report_field_group.offset]
; if report is numbered, add extra byte in the start of report
cmp [edx+report.id], 0
jz .scan_led_usage
add ebx, 8
.scan_led_usage:
; for USAGE_LED_*LOCK, store the current bit position in the corresponding field
; and store the current report as the LED report
cmp dword [esi], USAGE_LED_NUMLOCK
jz .numlock
cmp dword [esi], USAGE_LED_CAPSLOCK
jz .capslock
cmp dword [esi], USAGE_LED_SCROLLLOCK
jnz .next_field
.scrolllock:
mov [eax+keyboard_device_data.scrolllock_bit], ebx
jmp @f
.capslock:
mov [eax+keyboard_device_data.capslock_bit], ebx
jmp @f
.numlock:
mov [eax+keyboard_device_data.numlock_bit], ebx
@@:
mov [eax+keyboard_device_data.led_report], edx
.next_field:
add esi, 4
add ebx, [ecx+report_field_group.size]
dec dword [esp]
jnz .scan_led_usage
pop ebx
.next_led_field:
; 3g. Continue loop over field groups: get next field group.
mov ecx, [ecx+report_field_group.next]
jmp .scan_led_field
.next_led_report:
; 3h. If the LED report has been set, break from the loop over reports.
; Otherwise, get the next report and continue if the current report is not
; the last for this collection.
cmp [eax+keyboard_device_data.led_report], 0
jnz .led_report_set
cmp edx, [edi+collection.output.last_report]
mov edx, [edx+report.next]
jnz .scan_led_report
.led_report_set:
; 3i. Restore registers.
pop esi ebx
; 4. Register keyboard in the kernel.
; store pointer to keyboard_device_data in the stack
push eax
; call kernel API
stdcall RegKeyboard, kbd_functions, eax
; restore pointer to keyboard_device_data from the stack,
; putting keyboard handle from API to the stack
xchg eax, [esp]
; put keyboard handle from API from the stack to keyboard_device_data field
pop [eax+keyboard_device_data.handle]
; If failed, free keyboard_device_data and return NULL.
cmp [eax+keyboard_device_data.handle], 0
jz .fail_free
; 5. Return pointer to keyboard_device_data.
.nothing:
ret
.fail_free:
call Kfree
xor eax, eax
ret
endp
 
; This procedure is called when HID layer detects disconnect of a previously
; connected keyboard.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
proc keyboard_driver_disconnect
; 1. If an autorepeat timer is active, stop it.
cmp [edi+keyboard_device_data.timer], 0
jz @f
stdcall CancelTimerHS, [edi+keyboard_device_data.timer]
@@:
; 2. Unregister keyboard in the kernel.
stdcall DelKeyboard, [edi+keyboard_device_data.handle]
; We should free data in CloseKeyboard, not here.
ret
endp
 
; This procedure is called when HID layer starts processing a new input packet
; from a keyboard.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
proc keyboard_driver_begin_packet
; Nothing to do.
ret
endp
 
; This procedure is called when HID layer processes every non-empty array field group.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
; in: ecx = fields count (always nonzero), edx = pointer to fields values
; in: esi -> report_field_group
; out: CF set => group is ok, CF cleared => group should be ignored
proc keyboard_driver_array_overflow?
; The keyboard signals array overflow by filling the entire array with
; USAGE_KBD_ROLLOVER codes.
mov eax, [edx] ; eax = first field in the array
sub eax, USAGE_KBD_ROLLOVER ; eax = 0 if overflow, nonzero otherwise
neg eax ; CF cleared if eax was zero, CF set if eax was nonzero
ret
endp
 
; This procedure is called from HID layer for every field.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
; in: ecx = field usage, edx = value, esi -> report_field_group
proc keyboard_driver_input_field
if HID_DUMP_UNCLAIMED
.unclaimed = default_driver_input_field
end if
; 1. Process normal keys:
; from USAGE_KBD_FIRST_KEY to USAGE_KBD_FIRST_KEY + normal_keys_number - 1,
; excluding zeroes in [normal_keys].
; 1a. Test whether usage is in the range.
lea eax, [ecx-USAGE_KBD_FIRST_KEY]
cmp eax, normal_keys_number
jae .not_normal_key
; 1b. If the corresponding entry in [normal_keys] is zero,
; pass this field to the default handler - if HID_DUMP_UNCLAIMED is enabled,
; default handler is default_driver_input_field, otherwise just ignore the field.
cmp [normal_keys + eax], 0
jz .unclaimed
; 1c. Get the scancode.
movzx ecx, [normal_keys + eax]
; 1d. Further actions are slightly different for key press and key release.
; Decide what to do.
test edx, edx
jz .normal_key_released
.normal_key_pressed:
; The key is pressed.
; 1e. Store the last pressed key for autorepeat.
mov [edi+keyboard_device_data.repeatkey], cl
; 1f. Copy bit 7 to CF and send scancode with bit 7 cleared.
btr ecx, 7
call .send_key
; 1g. Stop the previous autorepeat timer, if any.
mov eax, [edi+keyboard_device_data.timer]
test eax, eax
jz @f
stdcall CancelTimerHS, eax
@@:
; 1h. Start the new autorepeat timer with 250 ms initial delay
; and 50 ms subsequent delays.
stdcall TimerHS, 25, 5, autorepeat_timer, edi
mov [edi+keyboard_device_data.timer], eax
if ~HID_DUMP_UNCLAIMED
.unclaimed:
end if
ret
.normal_key_released:
; The key is released.
; 1i. Stop the autorepeat timer if it is autorepeating the released key.
cmp [edi+keyboard_device_data.repeatkey], cl
jnz .no_stop_timer
push ecx
mov [edi+keyboard_device_data.repeatkey], 0
mov eax, [edi+keyboard_device_data.timer]
test eax, eax
jz @f
stdcall CancelTimerHS, eax
mov [edi+keyboard_device_data.timer], 0
@@:
pop ecx
.no_stop_timer:
; 1j. Copy bit 7 to CF and send scancode with bit 7 set.
bts ecx, 7
call .send_key
ret
.not_normal_key:
; 2. USAGE_KBD_NOEVENT is simply a filler for free array fields,
; ignore it.
cmp ecx, USAGE_KBD_NOEVENT
jz .nothing
; 3. Process modifiers: 8 keys starting at USAGE_KBD_LCTRL.
; 3a. Test whether usage is in range.
; If not, we don't know what this field means, so pass it to the default handler.
lea eax, [ecx-USAGE_KBD_LCTRL]
cmp eax, 8
jae .unclaimed
; 3b. Further actions are slightly different for modifier press
; and modifier release. Decide what to do.
test edx, edx
jz .modifier_not_pressed
.modifier_pressed:
; The modifier is pressed.
; 3c. Set the corresponding status bit.
; If it was not set, send the corresponding scancode to the kernel
; with bit 7 cleared.
bts [edi+keyboard_device_data.modifiers], eax
jc @f
movzx ecx, [control_keys+eax]
btr ecx, 7
call .send_key
@@:
.nothing:
ret
.modifier_not_pressed:
; The modifier is not pressed.
; 3d. Clear the correspodning status bit.
; If it was set, send the corresponding scancode to the kernel
; with bit 7 set.
btr [edi+keyboard_device_data.modifiers], eax
jnc @f
movzx ecx, [control_keys+eax]
bts ecx, 7
call .send_key
@@:
ret
 
; Helper procedure. Sends scancode from cl to the kernel.
; If CF is set, precede it with special code 0xE0.
.send_key:
jnc @f
push ecx
mov ecx, 0xE0
call SetKeyboardData
pop ecx
@@:
call SetKeyboardData
ret
endp
 
; This procedure is called when HID layer ends processing a new input packet
; from a keyboard.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
proc keyboard_driver_end_packet
; Nothing to do.
ret
endp
 
; Timer callback for SetTimerHS.
proc autorepeat_timer
virtual at esp
dd ? ; return address
.data dd ?
end virtual
; Just resend the last pressed key.
mov eax, [.data]
movzx ecx, [eax+keyboard_device_data.repeatkey]
; Copy bit 7 to CF and send scancode with bit 7 cleared.
btr ecx, 7
call keyboard_driver_input_field.send_key
ret 4
endp
 
; This function is called from the keyboard layer
; when it is safe to free keyboard data.
proc CloseKeyboard
virtual at esp
dd ? ; return address
.device_data dd ?
end virtual
mov eax, [.device_data]
call Kfree
ret 4
endp
 
; This function is called from the keyboard layer
; to update LED state on the keyboard.
proc SetKeyboardLights stdcall uses ebx esi edi, device_data, led_state
locals
size dd ?
endl
; 1. Get the pointer to the LED report.
; If there is no LED report, exit from the function.
mov ebx, [device_data]
mov esi, [ebx+keyboard_device_data.led_report]
test esi, esi
jz .nothing
; 2. Get report size in bytes.
; report.size is size in bits without possible report ID;
; if an ID is assigned, the size is one byte greater.
mov eax, [esi+report.size]
add eax, 7
shr eax, 3
cmp [esi+report.id], 0
jz @f
inc eax
@@:
mov [size], eax
; 3. Allocate memory for report + 8 bytes for setup packet.
; Dword-align size for subsequent rep stosd and bts.
; If failed, exit from the function.
add eax, 8 + 3
and eax, not 3
push eax
call Kmalloc
pop ecx
test eax, eax
jz .nothing
; 4. Zero-initialize output report.
push eax
mov edi, eax
shr ecx, 2
xor eax, eax
rep stosd
pop edi
add edi, 8
; 5. Store report ID, if assigned. If not assigned, that would just write zero
; over zeroes.
mov edx, [esi+report.id]
mov [edi], edx
; 6. Set report bits corresponding to active indicators.
mov eax, [led_state]
test al, 1 ; PS/2 Scroll Lock
jz @f
mov ecx, [ebx+keyboard_device_data.scrolllock_bit]
test ecx, ecx
js @f
bts [edi], ecx
@@:
test al, 2 ; PS/2 Num Lock
jz @f
mov ecx, [ebx+keyboard_device_data.numlock_bit]
test ecx, ecx
js @f
bts [edi], ecx
@@:
test al, 4 ; PS/2 Caps Lock
jz @f
mov ecx, [ebx+keyboard_device_data.capslock_bit]
test ecx, ecx
js @f
bts [edi], ecx
@@:
; 7. Fill setup packet.
shl edx, 16 ; move Report ID to byte 2
or edx, 21h + \ ; Class-specific request to Interface
(9 shl 8) + \ ; SET_REPORT
(2 shl 24) ; Report Type = Output
lea eax, [edi-8]
mov ebx, [ebx+keyboard_device_data.usbdev]
mov dword [eax], edx
mov edx, [size]
shl edx, 16 ; move Size to last word
or edx, [ebx+usb_device_data.interface_number]
mov [eax+4], edx
; 8. Submit output control request.
stdcall USBControlTransferAsync, [ebx+usb_device_data.configpipe], \
eax, edi, [size], after_set_keyboard_lights, ebx, 0
; If failed, free the buffer now.
; If succeeded, the callback will free the buffer.
test eax, eax
jnz .nothing
lea eax, [edi-8]
call Kfree
.nothing:
ret
endp
 
; This procedure is called from the USB subsystem when the request initiated by
; SetKeyboardLights is completed, either successfully or unsuccessfully.
proc after_set_keyboard_lights
virtual at esp
dd ? ; return address
.pipe dd ?
.status dd ?
.buffer dd ?
.length dd ?
.calldata dd ?
end virtual
; Ignore status, just free the buffer allocated by SetKeyboardLights.
mov eax, [.buffer]
sub eax, 8
call Kfree
ret 20
endp
/kernel/branches/Kolibri-acpi/drivers/usbhid/mouse.inc
0,0 → 1,155
; HID mouse driver, part of USBHID driver.
 
; Global constants.
; They are assembled in a macro to separate code and data;
; the code is located at the point of "include 'mouse.inc'",
; the data are collected when workers_globals is instantiated.
macro workers_globals
{
; include global constants from previous workers
workers_globals
align 4
; Callbacks for HID layer.
mouse_driver:
dd mouse_driver_add_device
dd mouse_driver_disconnect
dd mouse_driver_begin_packet
dd mouse_driver_array_overflow?
dd mouse_driver_input_field
dd mouse_driver_end_packet
}
 
; Data that are specific for one mouse device.
struct mouse_device_data
buttons dd ? ; buttons that are currently pressed
dx dd ? ; current x moving
dy dd ? ; current y moving
wheel dd ? ; current wheel moving
hwheel dd ?
ends
 
; This procedure is called when HID layer detects a new mouse.
; in: ebx -> device_data from USB layer, edi -> collection
; out: eax = device-specific data or NULL on error
proc mouse_driver_add_device
; Just allocate memory; no initialization needed.
movi eax, sizeof.mouse_device_data
call Kmalloc
ret
endp
 
; This procedure is called when HID layer detects disconnect of a previously
; connected mouse.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
proc mouse_driver_disconnect
; Free the allocated memory.
mov eax, edi
call Kfree
ret
endp
 
; This procedure is called when HID layer starts processing a new input packet
; from a mouse.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
proc mouse_driver_begin_packet
; Zero all variables describing the current state.
mov [edi+mouse_device_data.buttons], 0
mov [edi+mouse_device_data.dx], 0
mov [edi+mouse_device_data.dy], 0
mov [edi+mouse_device_data.wheel], 0
mov [edi+mouse_device_data.hwheel], 0
ret
endp
 
; This procedure is called when HID layer processes every non-empty array field group.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
; in: ecx = fields count (always nonzero), edx = pointer to fields values
; in: esi -> report_field_group
; out: CF set => array is ok, CF cleared => array should be ignored
proc mouse_driver_array_overflow?
; no array fields, no overflows
stc
ret
endp
 
; This procedure is called from HID layer for every field.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
; in: ecx = field usage, edx = value, esi -> report_field_group
proc mouse_driver_input_field
; 1. Determine the handler. We process x/y moving, wheel and up to 32 buttons.
; Pass other fields to the default handler - default_driver_input_field if
; HID_DUMP_UNCLAIMED is enabled, just ignore otherwise.
cmp ecx, USAGE_GD_X
jz .x
cmp ecx, USAGE_GD_Y
jz .y
cmp ecx, USAGE_GD_WHEEL
jz .wheel
cmp ecx, 0xC0238
jz .hwheel
sub ecx, USAGE_BUTTON_PAGE + 1
jb .unclaimed
cmp ecx, 32
jae .unclaimed
; 2. This is a button.
; If a button is pressed, set the corresponding bit in the state.
; If a button is not pressed, do nothing.
test edx, edx
jz @f
bts [edi+mouse_device_data.buttons], ecx
@@:
if ~HID_DUMP_UNCLAIMED
.unclaimed:
end if
ret
if HID_DUMP_UNCLAIMED
.unclaimed:
add ecx, USAGE_BUTTON_PAGE + 1
jmp default_driver_input_field
end if
.x:
; 3. This is x moving. For relative fields, store the value in the state.
; Pass absolute field to the default handler.
test byte [esi+report_field_group.flags], HID_FIELD_RELATIVE
jz .unclaimed
mov [edi+mouse_device_data.dx], edx
ret
.y:
; 4. This is y moving. For relative fields, store the value in the state,
; changing the sign: HID uses "mathematics" scheme with Y axis increasing from
; bottom to top, the kernel expects "programming" PS/2-style with Y axis
; increasing from top to bottom.
; Pass absolute fields to the default handler.
test byte [esi+report_field_group.flags], HID_FIELD_RELATIVE
jz .unclaimed
neg edx
mov [edi+mouse_device_data.dy], edx
ret
.wheel:
; 5. This is wheel event. For relative fields, store the value in the state,
; changing the sign. Pass absolute fields to the default handler.
test byte [esi+report_field_group.flags], HID_FIELD_RELATIVE
jz .unclaimed
neg edx
mov [edi+mouse_device_data.wheel], edx
ret
.hwheel:
test byte [esi+report_field_group.flags], HID_FIELD_RELATIVE
jz .unclaimed
mov [edi+mouse_device_data.hwheel], edx
ret
endp
 
; This procedure is called when HID layer ends processing a new input packet
; from a mouse.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
proc mouse_driver_end_packet
; Call the kernel, passing collected state.
stdcall SetMouseData, \
[edi+mouse_device_data.buttons], \
[edi+mouse_device_data.dx], \
[edi+mouse_device_data.dy], \
[edi+mouse_device_data.wheel], \
[edi+mouse_device_data.hwheel]
ret
endp
/kernel/branches/Kolibri-acpi/drivers/usbhid/report.inc
0,0 → 1,1442
; Parser of HID structures: parse HID report descriptor,
; parse/generate input/output/feature reports.
 
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; Usage codes from HID specification
; Generic Desktop usage page
USAGE_GD_POINTER = 10001h
USAGE_GD_MOUSE = 10002h
USAGE_GD_JOYSTICK = 10004h
USAGE_GD_GAMEPAD = 10005h
USAGE_GD_KEYBOARD = 10006h
USAGE_GD_KEYPAD = 10007h
 
USAGE_GD_X = 10030h
USAGE_GD_Y = 10031h
USAGE_GD_Z = 10032h
USAGE_GD_RX = 10033h
USAGE_GD_RY = 10034h
USAGE_GD_RZ = 10035h
USAGE_GD_SLIDER = 10036h
USAGE_GD_DIAL = 10037h
USAGE_GD_WHEEL = 10038h
 
; Keyboard/Keypad usage page
USAGE_KBD_NOEVENT = 70000h
USAGE_KBD_ROLLOVER = 70001h
USAGE_KBD_POSTFAIL = 70002h
USAGE_KBD_FIRST_KEY = 70004h ; this is 'A', actually
USAGE_KBD_LCTRL = 700E0h
USAGE_KBD_LSHIFT = 700E1h
USAGE_KBD_LALT = 700E2h
USAGE_KBD_LWIN = 700E3h
USAGE_KBD_RCTRL = 700E4h
USAGE_KBD_RSHIFT = 700E5h
USAGE_KBD_RALT = 700E6h
USAGE_KBD_RWIN = 700E7h
 
; LED usage page
USAGE_LED_NUMLOCK = 80001h
USAGE_LED_CAPSLOCK = 80002h
USAGE_LED_SCROLLLOCK = 80003h
 
; Button usage page
; First button is USAGE_BUTTON_PAGE+1, second - USAGE_BUTTON_PAGE+2 etc.
USAGE_BUTTON_PAGE = 90000h
 
; Flags for input/output/feature fields
HID_FIELD_CONSTANT = 1 ; if not, then Data field
HID_FIELD_VARIABLE = 2 ; if not, then Array field
HID_FIELD_RELATIVE = 4 ; if not, then Absolute field
HID_FIELD_WRAP = 8
HID_FIELD_NONLINEAR = 10h
HID_FIELD_NOPREFERRED= 20h ; no preferred state
HID_FIELD_HASNULL = 40h ; has null state
HID_FIELD_VOLATILE = 80h ; for output/feature fields
HID_FIELD_BUFBYTES = 100h; buffered bytes
 
; Report descriptor can easily describe gigabytes of (meaningless) data.
; Keep report size reasonable to avoid excessive memory allocations and
; calculation overflows; 1 Kb is more than enough (typical size is 3-10 bytes).
MAX_REPORT_BYTES = 1024
 
; =============================================================================
; ================================ Structures =================================
; =============================================================================
; Every meaningful report field group has one or more associated usages.
; Usages can be individual or joined into continuous ranges.
; This structure describes one range or one individual usage in a large array;
; individual usage is equivalent to a range of length 1.
struct usage_range
offset dd ?
; Sum of range sizes over all previous array items.
; Size of range a equals
; [a + sizeof.usage_range + usage_range.offset] - [a + usage_range.offset].
; The total sum over all array items immediately follows the array,
; this field must be the first so that the formula above works for the last item.
first_usage dd ?
; Usage code for first item in the range.
ends
 
; This structure describes one group of report fields with identical properties.
struct report_field_group
next dd ?
; All field groups in one report are organized in a single-linked list.
; This is the next group in the report or 0 for the last group.
size dd ?
; Size in bits of one field. Cannot be zero or greater than 32.
count dd ? ; field count, cannot be zero
offset dd ? ; offset from report start, in bits
; Following fields are decoded from report descriptor, see HID spec for details.
flags dd ?
logical_minimum dd ?
logical_maximum dd ?
physical_minimum dd ?
physical_maximum dd ?
unit_exponent dd ?
unit dd ?
; Following fields are used to speedup extract_field_value.
mask dd ?
; Bitmask for all data bits except sign bit:
; (1 shl .size) - 1 for unsigned fields, (1 shl (.size-1)) - 1 for signed fields
sign_mask dd ?
; Zero for unsigned fields. Bitmask with sign bit set for signed fields.
common_sizeof rd 0
; Variable and Array field groups differ significantly.
; Variable field groups are simple. There are .count fields, each field has
; predefined Usage, the content of a field is its value. Each field is
; always present in the report. For Variable field groups, we just keep
; additional .count dwords with usages for individual fields.
; Array field groups are complicated. There are .count uniform fields.
; The content of a field determines Usage; Usages which are currently presented
; in the report have value = 1, other Usages have value = 0. The number of
; possible Usages is limited only by field .size; 32-bit field could encode any
; Usage, so it is unreasonable to keep all Usages in the plain array, as with
; Variable fields. However, many unrelated Usages in one group are meaningless,
; so usually possible values are grouped in sequential ranges; number of ranges
; is limited by report descriptor size (max 0xFFFF bytes should contain all
; information, including usage ranges and field descriptions).
; Also, for Array variables we pass changes in state to drivers, not the state
; itself, because sending information about all possible Usages is inpractical;
; so we should remember the previous state in addition to the current state.
; Thus, for Array variables keep the following information, in this order:
; * some members listed below; note that they do NOT exist for Variable groups;
; * array of usage ranges in form of usage_range structures, including
; an additional dword after array described in usage_range structure;
; * allocated memory for current values of the report;
; * values of the previous report.
num_values_prev dd ? ; number of values in the previous report
num_usage_ranges dd ? ; number of usage_range, always nonzero
usages rd 0
ends
 
; This structure describes one report.
; All reports of one type are organized into a single-linked list.
struct report
next dd ? ; pointer to next report of the same type, if any
size dd ? ; total size in bits
first_field dd ? ; pointer to first report_field_group for this report
last_field dd ?
; pointer to last report_field_group for this report, if any;
; address of .first_field, if .first_field is 0
id dd ?
; Report ID, if assigned. Zero otherwise.
top_level_collection dd ? ; top-level collection for this report
ends
 
; This structure describes a set of reports of the same type;
; there are 3 sets (possibly empty), input, output and feature.
struct report_set
data dd ?
; If .numbered is zero, this is zero for the empty set and
; a pointer to the (only) report structure otherwise.
; If .numbered is nonzero, this is a pointer to 256-dword array of pointers
; to reports organized by report ID.
first_report dd ?
; Pointer to the first report or 0 for the empty set.
numbered db ?
; If zero, report IDs are not used, there can be at most one report in the set.
; If nonzero, first byte of the report is report ID.
rb 3 ; padding
ends
 
; This structure describes a range of reports of one type that belong to
; some collection.
struct collection_report_set
first_report dd ?
first_field dd ?
last_report dd ?
last_field dd ?
ends
 
; This structure defines driver callbacks which are used while
; device is active; i.e. all callbacks except add_device.
struct hid_driver_active_callbacks
disconnect dd ?
; Called when an existing HID device is disconnected.
;
; Four following functions are called when a new input packet arrives
; in the following order: .begin_packet, then .input_field several times
; for each input field, interleaved with .array_overflow? for array groups,
; then .end_packet.
begin_packet dd ?
; edi -> driver data
array_overflow? dd ?
; edi -> driver data
; out: CF cleared <=> ignore this array
input_field dd ?
; edi -> driver data, ecx = usage, edx = value
end_packet dd ?
; edi -> driver data
ends
 
; This structure describes one collection.
struct collection
next dd ? ; pointer to the next collection in the same level
; must be the first field
parent dd ? ; pointer to nesting collection
first_child dd ? ; pointer to the first nested collection
last_child dd ? ; pointer to the last nested collection
; or to .first_child, if .first_child is zero
type dd ? ; Application, Physical etc
usage dd ? ; associated Usage code
; Next fields are filled only for top-level collections.
callbacks hid_driver_active_callbacks
driver_data dd ? ; value to be passed as is to driver callbacks
input collection_report_set
output collection_report_set
feature collection_report_set
ends
 
; This structure keeps all data used by the HID layer for one device.
struct hid_data
input report_set
output report_set
feature report_set
first_collection dd ?
ends
 
; This structure defines callbacks required from the driver.
struct hid_driver_callbacks
add_device dd ?
; Called when a new HID device is connected.
active hid_driver_active_callbacks
ends
 
; Two following structures describe temporary data;
; the corresponding objects cease to exist when HID parser completes
; state of Global items
struct global_items
next dd ?
usage_page dd ?
logical_minimum dd ?
logical_maximum dd ?
physical_minimum dd ?
physical_maximum dd ?
unit_exponent dd ?
unit dd ?
report_size dd ?
report_id dd ?
report_count dd ?
ends
 
; one range of Usages
struct usage_list_item
next dd ?
first_usage dd ?
num_usages dd ?
ends
 
; =============================================================================
; =================================== Code ====================================
; =============================================================================
 
macro workers_globals
{
workers_globals
; Jump tables for switch'ing in the code.
align 4
; jump table for two lower bits which encode size of item data
parse_descr_label.fetch_jumps:
dd parse_descr_label.fetch_none ; x0, x4, x8, xC
dd parse_descr_label.fetch_byte ; x1, x5, x9, xD
dd parse_descr_label.fetch_word ; x2, x6, xA, xE
dd parse_descr_label.fetch_dword ; x3, x7, xB, xF
; jump table for two next bits which encode item type
parse_descr_label.type_jumps:
dd parse_descr_label.parse_main
dd parse_descr_label.parse_global
dd parse_descr_label.parse_local
dd parse_descr_label.parse_reserved
; jump table for 4 upper bits in the case of Main item
parse_descr_label.main_jumps:
dd parse_descr_label.input ; 80...83
dd parse_descr_label.output ; 90...93
dd parse_descr_label.collection ; A0...A3
dd parse_descr_label.feature ; B0...B3
dd parse_descr_label.end_collection ; C0...C3
parse_descr_label.num_main_items = ($ - parse_descr_label.main_jumps) / 4
; jump table for 4 upper bits in the case of Global item
parse_descr_label.global_jumps:
dd parse_descr_label.usage_page ; 04...07
dd parse_descr_label.logical_minimum ; 14...17
dd parse_descr_label.logical_maximum ; 24...27
dd parse_descr_label.physical_minimum ; 34...37
dd parse_descr_label.physical_maximum ; 44...47
dd parse_descr_label.unit_exponent ; 54...57
dd parse_descr_label.unit ; 64...67
dd parse_descr_label.report_size ; 74...77
dd parse_descr_label.report_id ; 84...87
dd parse_descr_label.report_count ; 94...97
dd parse_descr_label.push ; A4...A7
dd parse_descr_label.pop ; B4...B7
parse_descr_label.num_global_items = ($ - parse_descr_label.global_jumps) / 4
; jump table for 4 upper bits in the case of Local item
parse_descr_label.local_jumps:
dd parse_descr_label.usage ; 08...0B
dd parse_descr_label.usage_minimum ; 18...1B
dd parse_descr_label.usage_maximum ; 28...2B
dd parse_descr_label.item_parsed ; 38...3B = designator item; ignore
dd parse_descr_label.item_parsed ; 48...4B = designator minimum; ignore
dd parse_descr_label.item_parsed ; 58...5B = designator maximum; ignore
dd parse_descr_label.item_parsed ; 68...6B not assigned
dd parse_descr_label.item_parsed ; 78...7B = string index; ignore
dd parse_descr_label.item_parsed ; 88...8B = string minimum; ignore
dd parse_descr_label.item_parsed ; 98...9B = string maximum; ignore
dd parse_descr_label.delimiter ; A8...AB
parse_descr_label.num_local_items = ($ - parse_descr_label.local_jumps) / 4
}
 
; Local variables for parse_descr.
macro parse_descr_locals
{
cur_item_size dd ? ; encoded size of data for current item
report_ok db ? ; 0 on error, 1 if everything is ok
field_type db ? ; 0/1/2 for input/output/feature fields
rb 2 ; alignment
field_data dd ? ; data for current item when it describes a field group
last_reports rd 3 ; pointers to last input/output/feature records
usage_minimum dd ? ; current value of Usage Minimum
usage_list dd ? ; list head of usage_list_item
usage_tail dd ? ; list tail of usage_list_item
num_usage_ranges dd ? ; number of usage ranges, size of usage_list
delimiter_depth dd ? ; normally 0; 1 inside of Delimiter();
; nested Delimiter()s are not allowed
usage_variant dd ? ; 0 outside of Delimiter()s and for first Usage inside Delimiter(),
; incremented with each new Usage inside Delimiter()
cur_collection dd ? ; current collection
last_collection dd ? ; last top-level collection
}
 
; Parse report descriptor. The caller should provide local variables
; [buffer] = pointer to report descriptor, [length] = length of report descriptor,
; [calldata] = pointer to hid_data (possibly wrapped in a large structure).
macro parse_descr
{
parse_descr_label:
; 1. Initialize.
; 1a. Set some variables to initial values.
xor edi, edi
mov dword [report_ok], edi
mov [usage_list], edi
mov [cur_collection], edi
mov eax, [calldata]
add eax, hid_data.input.first_report
mov [last_reports+0*4], eax
add eax, hid_data.output.first_report - hid_data.input.first_report
mov [last_reports+1*4], eax
add eax, hid_data.feature.first_report - hid_data.output.first_report
mov [last_reports+2*4], eax
add eax, hid_data.first_collection - hid_data.feature.first_report
mov [last_collection], eax
; 1b. Allocate state of global items.
movi eax, sizeof.global_items
call Kmalloc
test eax, eax
jz .memory_error
; 1c. Zero-initialize it and move pointer to edi.
push eax
xchg eax, edi
movi ecx, sizeof.global_items / 4
rep stosd
pop edi
; 1d. Load pointer to data into esi and make [length] point to end of data.
mov esi, [buffer]
add [length], esi
; 2. Clear all local items.
; This is needed in the beginning and after processing any Main item.
.zero_local_items:
mov eax, [usage_list]
@@:
test eax, eax
jz @f
push [eax+usage_list_item.next]
call Kfree
pop eax
jmp @b
@@:
lea ecx, [usage_list]
mov [usage_tail], ecx
mov [ecx], eax
mov [delimiter_depth], eax
mov [usage_variant], eax
mov [usage_minimum], eax
mov [num_usage_ranges], eax
; 3. Parse items until end of data found.
cmp esi, [length]
jae .parse_end
.fetch_next_item:
; --------------------------------- Parse item --------------------------------
; 4. Parse one item.
; 4a. Get item data. eax = first item byte = code+type+size (4+2+2 bits),
; ebx = item data interpreted as unsigned,
; ecx = item data interpreted as signed.
movzx eax, byte [esi]
mov ecx, eax
and ecx, 3
mov [cur_item_size], ecx
jmp dword [.fetch_jumps+ecx*4]
.invalid_report:
mov esi, invalid_report_msg
jmp .end_str
.fetch_none:
xor ebx, ebx
xor ecx, ecx
inc esi
jmp .fetched
.fetch_byte:
add esi, 2
cmp esi, [length]
ja .invalid_report
movzx ebx, byte [esi-1]
movsx ecx, bl
jmp .fetched
.fetch_word:
add esi, 3
cmp esi, [length]
ja .invalid_report
movzx ebx, word [esi-2]
movsx ecx, bx
jmp .fetched
.fetch_dword:
add esi, 5
cmp esi, [length]
ja .invalid_report
mov ebx, dword [esi-4]
mov ecx, ebx
.fetched:
; 4b. Select the branch according to item type.
; For every type, select the concrete handler and go there.
mov edx, eax
shr edx, 2
and edx, 3
shr eax, 4
jmp dword [.type_jumps+edx*4]
; -------------------------------- Main items ---------------------------------
.parse_main:
sub eax, 8
cmp eax, .num_main_items
jae .item_parsed
jmp dword [.main_jumps+eax*4]
; There are 5 Main items.
; Input/Output/Feature items create new field groups in the corresponding report;
; Collection item opens a new collection (possibly nested),
; End Collection item closes the most nested collection.
.output:
mov [field_type], 1
jmp .new_field
.feature:
mov [field_type], 2
jmp .new_field
.input:
mov [field_type], 0
.new_field:
; Create a new field group.
mov [field_data], ebx
movzx ebx, [field_type]
if sizeof.report_set = 12
lea ebx, [ebx*3]
shl ebx, 2
else
err Change the code
end if
add ebx, [calldata]
; 5. Sanity checks: field size and fields count must be nonzero,
; field size cannot be more than 32 bits,
; if field count is more than MAX_REPORT_SIZE * 8, the report would be more than
; MAX_REPORT_SIZE bytes, so it is invalid too.
; More precise check for size occurs later; this check only guarantees that
; there will be no overflows during subsequent calculations.
cmp [edi+global_items.report_size], 0
jz .invalid_report
cmp [edi+global_items.report_size], 32
ja .invalid_report
; There are devices with Report Count(0) + Input(Constant Variable),
; zero-length padding. Thus, do not consider descriptors with Report Count(0)
; as invalid; instead, just ignore fields with Report Count(0).
cmp [edi+global_items.report_count], 0
jz .zero_local_items
cmp [edi+global_items.report_count], MAX_REPORT_BYTES * 8
ja .invalid_report
; 6. Get the pointer to the place for the corresponding report in ebx.
; 6a. If report ID is not assigned, ebx already points to report_set.data,
; so go to 7.
cmp [edi+global_items.report_id], 0
jz .report_ptr_found
; 6b. If table for reports was already allocated,
; go to 6d skipping the next substep.
cmp [ebx+report_set.numbered], 0
jnz .report_set_allocated
; 6c. This is the first report with ID;
; allocate and zero-initialize table for reports.
; Note: it is incorrect but theoretically possible that some fields were
; already allocated in report without ID; if so, abort processing with error.
cmp [ebx+report_set.data], 0
jnz .invalid_report
mov eax, 256*4
call Kmalloc
test eax, eax
jz .memory_error
mov [ebx+report_set.data], eax
inc [ebx+report_set.numbered]
push edi
mov edi, eax
mov ecx, 256
xor eax, eax
rep stosd
pop edi
; 6d. Report ID is assigned, report table is allocated,
; get the pointer to the corresponding item in the report table.
.report_set_allocated:
mov ebx, [ebx+report_set.data]
mov ecx, [edi+global_items.report_id]
lea ebx, [ebx+ecx*4]
; 7. If the field group is the first one in the report,
; allocate and initialize report without fields.
.report_ptr_found:
; 7a. Check whether the report has been allocated.
cmp dword [ebx], 0
jnz .report_allocated
; 7b. Allocate.
movi eax, sizeof.report
call Kmalloc
test eax, eax
jz .memory_error
; 7c. Initialize.
xor edx, edx
lea ecx, [eax+report.first_field]
mov [ebx], eax
mov [eax+report.next], edx
mov [eax+report.size], edx
mov [ecx], edx
mov [eax+report.last_field], ecx
mov [eax+report.top_level_collection], edx
mov ecx, [edi+global_items.report_id]
mov [eax+report.id], ecx
; 7d. Append to the overall list of reports.
movzx edx, [field_type]
lea edx, [last_reports+edx*4]
mov ecx, [edx]
mov [edx], eax
mov [ecx], eax
.report_allocated:
mov ebx, [ebx]
; ebx points to an already existing report; add new field.
; 8. Calculate total size of the group and
; check that the new group would not overflow the report.
mov eax, [edi+global_items.report_size]
mul [edi+global_items.report_count]
mov ecx, [ebx+report.size]
add ecx, eax
cmp ecx, MAX_REPORT_BYTES * 8
ja .invalid_report
; 9. If there are no usages for this group, this is padding;
; add it's size to total report size and stop processing.
cmp [num_usage_ranges], 0
jz .padding
; 10. Allocate memory for the group: this includes field group structure
; and additional fields depending on field type.
; See comments in report_field_group structure.
push eax
mov edx, [edi+global_items.report_count]
lea eax, [report_field_group.common_sizeof+edx*4]
test byte [field_data], HID_FIELD_VARIABLE
jnz @f
lea eax, [eax+edx*4]
mov edx, [num_usage_ranges]
lea eax, [eax+edx*sizeof.usage_range+4]
@@:
call Kmalloc
pop edx
test eax, eax
jz .memory_error
; 11. Update report data.
; Field offset is the current report size;
; get the current report size and update report size.
; Also store the pointer to new field in the previous last field
; and update the last field.
mov ecx, [ebx+report.last_field]
xadd [ebx+report.size], edx
mov [ebx+report.last_field], eax
mov [ecx], eax
; 12. Initialize field data: offset was calculated in the previous step,
; copy other characteristics from global_items data,
; calculate .mask and .sign_mask.
mov [eax+report_field_group.offset], edx
xor edx, edx
mov [eax+report_field_group.next], edx
mov [eax+report_field_group.sign_mask], edx
inc edx
mov ecx, [edi+global_items.report_size]
mov [eax+report_field_group.size], ecx
shl edx, cl
cmp [edi+global_items.logical_minimum], 0
jge .unsigned
shr edx, 1
mov [eax+report_field_group.sign_mask], edx
.unsigned:
dec edx
mov [eax+report_field_group.mask], edx
mov ecx, [edi+global_items.report_count]
mov [eax+report_field_group.count], ecx
mov ecx, [field_data]
mov [eax+report_field_group.flags], ecx
irps field, logical_minimum logical_maximum physical_minimum physical_maximum unit_exponent unit
\{
mov ecx, [edi+global_items.\#field]
mov [eax+report_field_group.\#field], ecx
\}
; 13. Update the current collection; nesting collections will be updated by
; end-of-collection handler.
movzx edx, [field_type]
if sizeof.collection_report_set = 16
shl edx, 4
else
err Change the code
end if
mov ecx, [cur_collection]
test ecx, ecx
jz .no_collection
lea ecx, [ecx+collection.input+edx]
mov [ecx+collection_report_set.last_report], ebx
mov [ecx+collection_report_set.last_field], eax
cmp [ecx+collection_report_set.first_field], 0
jnz .no_collection
mov [ecx+collection_report_set.first_report], ebx
mov [ecx+collection_report_set.first_field], eax
.no_collection:
; 14. Transform usage ranges. The target format depends on field type.
test byte [eax+report_field_group.flags], HID_FIELD_VARIABLE
jz .transform_usages_for_array
; For Variable field groups, expand all ranges to array with .count Usages.
; If total number of Usages in all ranges is too large, ignore excessive.
; If total number of Usages in all ranges is too small, duplicate the last
; Usage up to .count Usages (e.g. group of several indicators can have one usage
; "Generic Indicator" assigned to all fields).
mov ecx, [eax+report_field_group.count]
mov ebx, [usage_list]
.next_usage_range_for_variable:
mov edx, [ebx+usage_list_item.first_usage]
push [ebx+usage_list_item.num_usages]
.next_usage_for_variable:
mov [eax+report_field_group.common_sizeof], edx
dec ecx
jz @f
add eax, 4
inc edx
dec dword [esp]
jnz .next_usage_for_variable
dec edx
inc dword [esp]
cmp [ebx+usage_list_item.next], 0
jz .next_usage_for_variable
pop edx
mov ebx, [ebx+usage_list_item.next]
jmp .next_usage_range_for_variable
@@:
pop ebx
jmp .zero_local_items
.transform_usages_for_array:
; For Array field groups, leave ranges unexpanded, but recode in the form
; more convenient to value lookup, see comments in report_field_group structure.
mov ecx, [num_usage_ranges]
mov [eax+report_field_group.num_usage_ranges], ecx
and [eax+report_field_group.num_values_prev], 0
mov ecx, [usage_list]
xor ebx, ebx
@@:
mov edx, [ecx+usage_list_item.first_usage]
mov [eax+report_field_group.usages+usage_range.offset], ebx
add ebx, [ecx+usage_list_item.num_usages]
jc .invalid_report
mov [eax+report_field_group.usages+usage_range.first_usage], edx
add eax, sizeof.usage_range
mov ecx, [ecx+usage_list_item.next]
test ecx, ecx
jnz @b
mov [eax+report_field_group.usages], ebx
; New field is initialized.
jmp .zero_local_items
.padding:
mov [ebx+report.size], ecx
jmp .zero_local_items
; Create a new collection, nested in the current one.
.collection:
; Actions are quite straightforward:
; allocate, zero-initialize, update parent, if there is one,
; make it current.
movi eax, sizeof.collection
call Kmalloc
test eax, eax
jz .memory_error
push eax edi
movi ecx, sizeof.collection / 4
xchg edi, eax
xor eax, eax
rep stosd
pop edi eax
mov edx, [cur_collection]
mov [eax+collection.parent], edx
lea ecx, [last_collection]
test edx, edx
jz .no_parent
lea ecx, [edx+collection.last_child]
.no_parent:
mov edx, [ecx]
mov [ecx], eax
mov [edx], eax
lea ecx, [eax+collection.first_child]
; In theory, there must be at least one usage.
; In practice, some nested collections don't have any. Use zero in this case.
mov edx, [usage_list]
test edx, edx
jz @f
mov edx, [edx+usage_list_item.first_usage]
@@:
mov [eax+collection.last_child], ecx
mov [eax+collection.type], ebx
mov [eax+collection.usage], edx
mov [cur_collection], eax
jmp .zero_local_items
; Close the current collection.
.end_collection:
; There must be an opened collection.
mov eax, [cur_collection]
test eax, eax
jz .invalid_report
; Make parent collection the current one.
mov edx, [eax+collection.parent]
mov [cur_collection], edx
; Add field range of the closing collection to field range for nesting collection,
; if there is one.
test edx, edx
jz .zero_local_items
push 3 ; for each type: input, output, feature
.update_ranges:
mov ecx, [eax+collection.input.last_report]
test ecx, ecx
jz .no_fields
mov [edx+collection.input.last_report], ecx
mov ecx, [eax+collection.input.last_field]
mov [edx+collection.input.last_field], ecx
cmp [edx+collection.input.first_report], 0
jnz .no_fields
mov ecx, [eax+collection.input.first_report]
mov [edx+collection.input.first_report], ecx
mov ecx, [eax+collection.input.first_field]
mov [edx+collection.input.first_field], ecx
.no_fields:
add eax, sizeof.collection_report_set
add edx, sizeof.collection_report_set
dec dword [esp]
jnz .update_ranges
pop eax
jmp .zero_local_items
; ------------------------------- Global items --------------------------------
.parse_global:
cmp eax, .num_global_items
jae .item_parsed
jmp dword [.global_jumps+eax*4]
; For most global items, just store the value in the current global_items structure.
; Note 1: Usage Page will be used for upper word of Usage[| Minimum|Maximum], so
; shift it in advance.
; Note 2: the HID specification allows both signed and unsigned values for
; logical and physical minimum/maximum, but does not give a method to distinguish.
; Thus, hope that minimum comes first, parse the minimum as signed value always,
; if it is less than zero, assume signed values, otherwise assume unsigned values.
; This covers both common cases Minimum(0)/Maximum(FF) and Minimum(-7F)/Maximum(7F).
; Note 3: zero value for Report ID is forbidden by the HID specification.
; It is quite convenient, we use report_id == 0 for reports without ID.
.usage_page:
shl ebx, 16
mov [edi+global_items.usage_page], ebx
jmp .item_parsed
.logical_minimum:
mov [edi+global_items.logical_minimum], ecx
jmp .item_parsed
.logical_maximum:
cmp [edi+global_items.logical_minimum], 0
jge @f
mov ebx, ecx
@@:
mov [edi+global_items.logical_maximum], ebx
jmp .item_parsed
.physical_minimum:
mov [edi+global_items.physical_minimum], ecx
jmp .item_parsed
.physical_maximum:
cmp [edi+global_items.physical_maximum], 0
jge @f
mov ebx, ecx
@@:
mov [edi+global_items.physical_maximum], ebx
jmp .item_parsed
.unit_exponent:
mov [edi+global_items.unit_exponent], ecx
jmp .item_parsed
.unit:
mov [edi+global_items.unit], ebx
jmp .item_parsed
.report_size:
mov [edi+global_items.report_size], ebx
jmp .item_parsed
.report_id:
test ebx, ebx
jz .invalid_report
cmp ebx, 0x100
jae .invalid_report
mov [edi+global_items.report_id], ebx
jmp .item_parsed
.report_count:
mov [edi+global_items.report_count], ebx
jmp .item_parsed
; Two special global items: Push/Pop.
.push:
; For Push, allocate new global_items structure,
; initialize from the current one and make it current.
movi eax, sizeof.global_items
call Kmalloc
test eax, eax
jz .memory_error
push esi eax
movi ecx, sizeof.global_items / 4
mov esi, edi
xchg eax, edi
rep movsd
pop edi esi
mov [edi+global_items.next], eax
jmp .item_parsed
.pop:
; For Pop, restore the last global_items structure and free the current one.
mov eax, [edi+global_items.next]
test eax, eax
jz .invalid_report
push eax
xchg eax, edi
call Kfree
pop edi
jmp .item_parsed
; -------------------------------- Local items --------------------------------
.parse_local:
cmp eax, .num_local_items
jae .item_parsed
jmp dword [.local_jumps+eax*4]
.usage:
; Usage tag.
; If length is 0, 1, 2 bytes, append the global item Usage Page.
cmp [cur_item_size], 2
ja @f
or ebx, [edi+global_items.usage_page]
@@:
; If inside Delimiter(), ignore everything except the first tag.
cmp [delimiter_depth], 0
jz .usage.write
inc [usage_variant]
cmp [usage_variant], 1
jnz .item_parsed
.usage.write:
; Add new range with start = item data and length = 1.
mov [usage_minimum], ebx
push 1
.new_usage:
movi eax, sizeof.usage_list_item
call Kmalloc
pop edx
test eax, eax
jz .memory_error
inc [num_usage_ranges]
mov ecx, [usage_minimum]
and [eax+usage_list_item.next], 0
mov [eax+usage_list_item.first_usage], ecx
mov [eax+usage_list_item.num_usages], edx
mov ecx, [usage_tail]
mov [usage_tail], eax
mov [ecx], eax
jmp .item_parsed
.usage_minimum:
; Usage Minimum tag. Just store in the local var.
; If length is 0, 1, 2 bytes, append the global item Usage Page.
cmp [cur_item_size], 2
ja @f
or ebx, [edi+global_items.usage_page]
@@:
mov [usage_minimum], ebx
jmp .item_parsed
.usage_maximum:
; Usage Maximum tag.
; If length is 0, 1, 2 bytes, append the global item Usage Page.
cmp [cur_item_size], 2
ja @f
or ebx, [edi+global_items.usage_page]
@@:
; Meaningless inside Delimiter().
cmp [delimiter_depth], 0
jnz .invalid_report
; Add new range with start = saved Usage Minimum and
; length = Usage Maximum - Usage Minimum + 1.
sub ebx, [usage_minimum]
inc ebx
push ebx
jmp .new_usage
.delimiter:
; Delimiter tag.
test ebx, ebx
jz .delimiter.close
; Delimiter(Opened).
; Store that we are inside Delimiter(),
; say a warning that only preferred Usage will be used.
cmp [delimiter_depth], 0
jnz .invalid_report
inc [delimiter_depth]
push esi
mov esi, delimiter_note
call SysMsgBoardStr
pop esi
jmp .item_parsed
.delimiter.close:
; Delimiter(Closed).
; Store that we are not inside Delimiter() anymore.
dec [delimiter_depth]
js .invalid_report
and [usage_variant], 0
jmp .item_parsed
.parse_reserved:
; Ignore reserved items, except that tag 0xFE means long item
; with first data byte = length of additional data,
; second data byte = long item tag. No long items are defined yet,
; so just skip them.
cmp eax, 0xF
jnz .item_parsed
cmp [cur_item_size], 2
jnz .item_parsed
movzx ecx, bl
add esi, ecx
cmp esi, [length]
ja .invalid_report
.item_parsed:
cmp esi, [length]
jb .fetch_next_item
.parse_end:
;-------------------------------- End of parsing ------------------------------
; If there are opened collections, it is invalid report.
cmp [cur_collection], 0
jnz .invalid_report
; There must be at least one input field.
mov eax, [calldata]
add eax, hid_data.input.first_report
cmp [last_reports+0*4], eax
jz .invalid_report
; Everything is ok.
inc [report_ok]
jmp .end
.memory_error:
mov esi, nomemory_msg
.end_str:
call SysMsgBoardStr
.end:
; Free all global_items structures.
test edi, edi
jz @f
push [edi+global_items.next]
xchg eax, edi
call Kfree
pop edi
jmp .end
@@:
; Free the last Usage list, if any.
mov eax, [usage_list]
@@:
test eax, eax
jz @f
push [eax+usage_list_item.next]
call Kfree
pop eax
jmp @b
@@:
}
 
; Assign drivers to top-level HID collections.
; The caller should provide ebx = pointer to hid_data and a local variable
; [has_driver], it will be initialized with 0 if no driver is present.
macro postprocess_descr
{
postprocess_report_label:
; Assign drivers to top-level collections.
; Use mouse driver for Usage(GenericDesktop:Mouse),
; use keyboard driver for Usage(GenericDesktop:Keyboard)
; and Usage(GenericDesktop:Keypad)
; 1. Prepare for the loop: get the pointer to the first collection,
; store that no drivers were assigned yet.
mov edi, [ebx+hid_data.first_collection]
if ~HID_DUMP_UNCLAIMED
mov [has_driver], 0
end if
.next_collection:
; 2. Test whether there is a collection to test; if no, break from the loop.
test edi, edi
jz .postprocess_done
; 3. Get pointer to driver callbacks depending on [collection.usage].
; If [collection.usage] is unknown, use default driver if HID_DUMP_UNCLAIMED
; and do not assign a driver otherwise.
mov esi, mouse_driver
cmp [edi+collection.usage], USAGE_GD_MOUSE
jz .has_driver
mov esi, keyboard_driver
cmp [edi+collection.usage], USAGE_GD_KEYBOARD
jz .has_driver
cmp [edi+collection.usage], USAGE_GD_KEYPAD
jz .has_driver
if HID_DUMP_UNCLAIMED
mov esi, default_driver
else
xor esi, esi
end if
; 4. If no driver is assigned (possible only if not HID_DUMP_UNCLAIMED),
; go to 7 with driver data = 0;
; other code uses this as a sign that driver callbacks should not be called.
.has_driver:
xor eax, eax
if ~HID_DUMP_UNCLAIMED
test esi, esi
jz .set_driver
end if
; 5. Notify the driver about new device.
call [esi+hid_driver_callbacks.add_device]
; 6. If the driver has returned non-zero driver data,
; store that is an assigned driver.
; Otherwise, if HID_DUMP_UNCLAIMED, try to assign the default driver.
if HID_DUMP_UNCLAIMED
test eax, eax
jnz .set_driver
mov esi, default_driver
call [esi+hid_driver_callbacks.add_device]
else
test eax, eax
jz @f
mov [has_driver], 1
jmp .set_driver
@@:
xor esi, esi
end if
.set_driver:
; 7. Store driver data. If a driver is assigned, copy driver callbacks.
mov [edi+collection.driver_data], eax
test esi, esi
jz @f
push edi
lodsd ; skip hid_driver_callbacks.add_device
add edi, collection.callbacks
repeat sizeof.hid_driver_active_callbacks / 4
movsd
end repeat
pop edi
@@:
; 8. Store pointer to the collection in all input reports belonging to it.
; Note that the HID spec requires that reports should not cross top-level collections.
mov eax, [edi+collection.input.first_report]
test eax, eax
jz .reports_processed
.next_report:
mov [eax+report.top_level_collection], edi
cmp eax, [edi+collection.input.last_report]
mov eax, [eax+report.next]
jnz .next_report
.reports_processed:
mov edi, [edi+collection.next]
jmp .next_collection
.postprocess_done:
}
 
; Cleanup all resources allocated during parse_descr and postprocess_descr.
; Called when the corresponding device is disconnected
; with ebx = pointer to hid_data.
macro hid_cleanup
{
; 1. Notify all assigned drivers about disconnect.
; Loop over all top-level collections and call callbacks.disconnect,
; if a driver is assigned.
mov esi, [ebx+hid_data.first_collection]
.notify_drivers:
test esi, esi
jz .notify_drivers_done
mov edi, [esi+collection.driver_data]
test edi, edi
jz @f
call [esi+collection.callbacks.disconnect]
@@:
mov esi, [esi+collection.next]
jmp .notify_drivers
.notify_drivers_done:
; 2. Free all collections.
mov esi, [ebx+hid_data.first_collection]
.free_collections:
test esi, esi
jz .collections_done
; If a collection has childen, make it forget about them,
; kill all children; after last child is killed, return to
; the collection as a parent; this time, it will appear
; as childless, so it will be killed after children.
mov eax, [esi+collection.first_child]
test eax, eax
jz .no_children
and [esi+collection.first_child], 0
xchg esi, eax
jmp .free_collections
.no_children:
; If a collection has no children (maybe there were no children at all,
; maybe all children were already killed), kill it and proceed either to
; next sibling (if any) or to the parent.
mov eax, [esi+collection.next]
test eax, eax
jnz @f
mov eax, [esi+collection.parent]
@@:
xchg eax, esi
call Kfree
jmp .free_collections
.collections_done:
; 3. Free all three report sets.
push 3
lea esi, [ebx+hid_data.input]
; For every report set, loop over all reports,
; for every report free all field groups, then free report itself.
; When all reports in one set have been freed, free also report list table,
; if there is one (reports are numbered).
.report_set_loop:
mov edi, [esi+report_set.first_report]
.report_loop:
test edi, edi
jz .report_done
mov eax, [edi+report.first_field]
.field_loop:
test eax, eax
jz .field_done
push [eax+report_field_group.next]
call Kfree
pop eax
jmp .field_loop
.field_done:
mov eax, [edi+report.next]
xchg eax, edi
call Kfree
jmp .report_loop
.report_done:
cmp [esi+report_set.numbered], 0
jz @f
mov eax, [esi+report_set.data]
call Kfree
@@:
add esi, sizeof.report_set
dec dword [esp]
jnz .report_set_loop
pop eax
}
 
; Helper for parse_input. Extracts value of one field.
; in: esi -> report_field_group
; in: eax = offset in bits from report start
; in: report -> report data
; out: edx = value
; Note: it can read one dword past report data.
macro extract_field_value report
{
mov ecx, eax
shr eax, 5
shl eax, 2
add eax, report
and ecx, 31
mov edx, [eax]
mov eax, [eax+4]
shrd edx, eax, cl
mov ecx, [esi+report_field_group.sign_mask]
and ecx, edx
and edx, [esi+report_field_group.mask]
sub edx, ecx
}
 
; Local variables for parse_input.
macro parse_input_locals
{
count_inside_group dd ?
; Number of fields left in the current field.
field_offset dd ?
; Offset of the current field from report start, in bits.
field_range_size dd ?
; Size of range with valid values, Logical Maximum - Logical Minimum + 1.
cur_usage dd ?
; Pointer to current usage for Variable field groups.
num_values dd ?
; Number of values in the current instantiation of Array field group.
values_base dd ?
; Pointer to memory allocated for array with current values.
values_prev dd ?
; Pointer to memory allocated for array with previous values.
values_cur_ptr dd ?
; Pointer to the next value in [values_base] array.
values_end dd ?
; End of data in array with current values.
values_prev_ptr dd ?
; Pointer to the next value in [values_prev_ptr] array.
values_prev_end dd ?
; End of data in array with previous values.
}
 
; Parse input report. The caller should provide esi = pointer to report,
; local variables parse_input_locals and [buffer] = report data.
macro parse_input
{
; 1. Ignore the report if there is no driver for it.
mov ebx, [esi+report.top_level_collection]
mov edi, [ebx+collection.driver_data]
test edi, edi
jz .done
; 2. Notify the driver that a new packet arrived.
call [ebx+collection.callbacks.begin_packet]
; Loop over all field groups.
; Report without fields is meaningless, but theoretically possible:
; parse_descr does not create reports of zero size, but
; a report can consist of "padding" fields without usages and have
; no real fields.
mov esi, [esi+report.first_field]
test esi, esi
jz .packet_processed
.field_loop:
; 3. Prepare for group handling: initialize field offset, fields count
; and size of range for valid values.
mov eax, [esi+report_field_group.offset]
mov [field_offset], eax
mov ecx, [esi+report_field_group.count]
mov [count_inside_group], ecx
mov eax, [esi+report_field_group.logical_maximum]
inc eax
sub eax, [esi+report_field_group.logical_minimum]
mov [field_range_size], eax
; 4. Select handler. Variable and Array groups are handled entirely differently;
; for Variable groups, advance to 5, for Array groups, go to 6.
test byte [esi+report_field_group.flags], HID_FIELD_VARIABLE
jz .array_field
; 5. Variable groups. They are simple. Loop over all .count fields,
; for every field extract the value and get the next usage,
; if the value is within valid range, call the driver.
lea eax, [esi+report_field_group.common_sizeof]
mov [cur_usage], eax
.variable_data_loop:
mov eax, [field_offset]
extract_field_value [buffer] ; -> edx
mov ecx, [cur_usage]
mov ecx, [ecx]
call [ebx+collection.callbacks.input_field]
add [cur_usage], 4
mov eax, [esi+report_field_group.size]
add [field_offset], eax
dec [count_inside_group]
jnz .variable_data_loop
; Variable group is processed; go to 12.
jmp .field_done
.array_field:
; Array groups. They are complicated.
; 6. Array group: extract all values in one array.
; memory was allocated during group creation, use it
; 6a. Prepare: get data pointer, initialize num_values with zero.
mov eax, [esi+report_field_group.num_usage_ranges]
lea edx, [esi+report_field_group.usages+eax*sizeof.usage_range+4]
mov eax, [esi+report_field_group.count]
mov [values_cur_ptr], edx
mov [values_base], edx
lea edx, [edx+ecx*4]
mov [values_prev], edx
mov [values_prev_ptr], edx
mov [num_values], 0
; 6b. Start loop for every field. Note that there must be at least one field,
; parse_descr does not allow .count == 0.
.array_getval_loop:
; 6c. Extract the value of the current field.
mov eax, [field_offset]
extract_field_value [buffer] ; -> edx
; 6d. Transform the value to the usage with binary search in array of
; usage_ranges. started at [esi+report_field_group.usages]
; having [esi+report_field_group.num_usage_ranges] items.
; Ignore items outside of valid range.
sub edx, [esi+report_field_group.logical_minimum]
cmp edx, [field_range_size]
jae .array_skip_item
; If there are too few usages, use last of them.
mov ecx, [esi+report_field_group.num_usage_ranges] ; upper bound
xor eax, eax ; lower bound
cmp edx, [esi+report_field_group.usages+ecx*sizeof.usage_range+usage_range.offset]
jae .array_last_usage
; loop invariant: usages[eax].offset <= edx < usages[ecx].offset
.array_find_usage:
lea edi, [eax+ecx]
shr edi, 1
cmp edi, eax
jz .array_found_usage_range
cmp edx, [esi+report_field_group.usages+edi*sizeof.usage_range+usage_range.offset]
jae .update_low
mov ecx, edi
jmp .array_find_usage
.update_low:
mov eax, edi
jmp .array_find_usage
.array_last_usage:
lea eax, [ecx-1]
mov edx, [esi+report_field_group.usages+ecx*sizeof.usage_range+usage_range.offset]
dec edx
.array_found_usage_range:
sub edx, [esi+report_field_group.usages+eax*sizeof.usage_range+usage_range.offset]
add edx, [esi+report_field_group.usages+eax*sizeof.usage_range+usage_range.first_usage]
; 6e. Store the usage, advance data pointer, continue loop started at 6b.
mov eax, [values_cur_ptr]
mov [eax], edx
add [values_cur_ptr], 4
inc [num_values]
.array_skip_item:
mov eax, [esi+report_field_group.size]
add [field_offset], eax
dec [count_inside_group]
jnz .array_getval_loop
; 7. Array group: ask driver about array overflow.
; If driver says that the array is invalid, stop processing this group
; (in particular, do not update previous values).
mov ecx, [num_values]
test ecx, ecx
jz .duplicates_removed
mov edx, [values_base]
mov edi, [ebx+collection.driver_data]
call [ebx+collection.callbacks.array_overflow?]
jnc .field_done
; 8. Array group: sort the array with current values.
push esi
mov ecx, [num_values]
mov edx, [values_base]
call sort
pop esi
; 9. Array group: remove duplicates.
cmp [num_values], 1
jbe .duplicates_removed
mov eax, [values_base]
mov edx, [eax]
add eax, 4
mov ecx, eax
.duplicates_loop:
cmp edx, [eax]
jz @f
mov edx, [eax]
mov [ecx], edx
add ecx, 4
@@:
add eax, 4
cmp eax, [values_cur_ptr]
jb .duplicates_loop
mov [values_cur_ptr], ecx
sub ecx, [values_base]
shr ecx, 2
mov [num_values], ecx
.duplicates_removed:
; 10. Array group: compare current and previous values,
; call driver for differences.
mov edi, [ebx+collection.driver_data]
mov eax, [values_cur_ptr]
mov [values_end], eax
mov eax, [values_base]
mov [values_cur_ptr], eax
mov eax, [esi+report_field_group.num_values_prev]
shl eax, 2
add eax, [values_prev]
mov [values_prev_end], eax
.find_common:
mov eax, [values_cur_ptr]
cmp eax, [values_end]
jae .cur_done
mov ecx, [eax]
mov eax, [values_prev_ptr]
cmp eax, [values_prev_end]
jae .prev_done
mov edx, [eax]
cmp ecx, edx
jb .advance_cur
ja .advance_prev
; common item in both arrays; ignore
add [values_cur_ptr], 4
add [values_prev_ptr], 4
jmp .find_common
.advance_cur:
; item is present in current array but not in previous;
; call the driver with value = 1
add [values_cur_ptr], 4
mov edx, 1
call [ebx+collection.callbacks.input_field]
jmp .find_common
.advance_prev:
; item is present in previous array but not in current;
; call the driver with value = 0
add [values_prev_ptr], 4
mov ecx, edx
xor edx, edx
call [ebx+collection.callbacks.input_field]
jmp .find_common
.prev_done:
; for all items which are left in current array
; call the driver with value = 1
mov eax, [values_cur_ptr]
@@:
add [values_cur_ptr], 4
mov ecx, [eax]
mov edx, 1
call [ebx+collection.callbacks.input_field]
mov eax, [values_cur_ptr]
cmp eax, [values_end]
jb @b
jmp .copy_array
.cur_done:
; for all items which are left in previous array
; call the driver with value = 0
mov eax, [values_prev_ptr]
add [values_prev_ptr], 4
cmp eax, [values_prev_end]
jae @f
mov ecx, [eax]
xor edx, edx
call [ebx+collection.callbacks.input_field]
jmp .cur_done
@@:
.copy_array:
; 11. Array group: copy current values to previous values.
push esi edi
mov ecx, [num_values]
mov [esi+report_field_group.num_values_prev], ecx
mov esi, [values_base]
mov edi, [values_prev]
rep movsd
pop edi esi
; 12. Field group is processed. Repeat with the next group, if any.
.field_done:
mov esi, [esi+report_field_group.next]
test esi, esi
jnz .field_loop
.packet_processed:
; 13. Packet is processed, notify the driver.
call [ebx+collection.callbacks.end_packet]
}
/kernel/branches/Kolibri-acpi/drivers/usbhid/sort.inc
0,0 → 1,60
; Sort array of unsigned dwords in non-decreasing order.
; ecx = array size, edx = array pointer.
; Destroys eax, ecx, esi, edi.
sort:
test ecx, ecx
jz .done
mov eax, ecx
@@:
push eax
call .restore
pop eax
dec eax
jnz @b
@@:
cmp ecx, 1
jz .done
mov esi, 1
mov edi, ecx
call .exchange
dec ecx
mov eax, 1
call .restore
jmp @b
.done:
ret
 
.exchange:
push eax ecx
mov eax, [edx+esi*4-4]
mov ecx, [edx+edi*4-4]
mov [edx+esi*4-4], ecx
mov [edx+edi*4-4], eax
pop ecx eax
ret
 
.restore:
lea esi, [eax+eax]
cmp esi, ecx
ja .doner
mov edi, [edx+eax*4-4]
cmp [edx+esi*4-4], edi
ja .need_xchg
cmp esi, ecx
jae .doner
mov edi, [edx+eax*4-4]
cmp [edx+esi*4], edi
jbe .doner
.need_xchg:
cmp esi, ecx
jz .do_xchg
mov edi, [edx+esi*4-4]
cmp [edx+esi*4], edi
sbb esi, -1
.do_xchg:
mov edi, eax
call .exchange
mov eax, esi
jmp .restore
.doner:
ret
/kernel/branches/Kolibri-acpi/drivers/usbhid/unclaimed.inc
0,0 → 1,60
; HID default driver, part of USBHID driver.
; Present only if compile-time setting HID_DUMP_UNCLAIMED is on.
; Active for those devices when we do not have a specialized driver.
; Just dumps everything to the debug board.
 
if HID_DUMP_UNCLAIMED
; Global constants.
; They are assembled in a macro to separate code and data;
; the code is located at the point of "include 'unclaimed.inc'",
; the data are collected when workers_globals is instantiated.
macro workers_globals
{
; include global constants from previous workers
workers_globals
align 4
; Callbacks for HID layer.
default_driver:
dd default_driver_add_device
dd default_driver_disconnect
dd default_driver_begin_packet
dd default_driver_array_overflow?
dd default_driver_input_field
dd default_driver_end_packet
}
; This procedure is called when HID layer detects a new driverless device.
; in: ebx -> usb_device_data, edi -> collection
; out: eax = device-specific data or NULL on error
default_driver_add_device:
; just return something nonzero, no matter what
xor eax, eax
inc eax
ret
; This procedure is called when HID layer processes every non-empty array field group.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
; in: ecx = fields count (always nonzero), edx = pointer to fields values
; in: esi -> report_field_group
; out: CF set => group is ok, CF cleared => group should be ignored
default_driver_array_overflow?:
; parse everything
stc
ret
; This procedure is called from HID layer for every field.
; in: ecx = field usage, edx = value, esi -> report_field_group
default_driver_input_field:
; Do not dump zero values in Variable fields,
; they are present even if the corresponding control is inactive.
test edx, edx
jnz @f
test byte [esi+report_field_group.flags], HID_FIELD_VARIABLE
jnz .nodump
@@:
DEBUGF 1,'K : unclaimed HID input: usage=%x, value=%x\n',ecx,edx
.nodump:
; pass through
; Three nothing-to-do procedures.
default_driver_disconnect:
default_driver_begin_packet:
default_driver_end_packet:
ret
end if
/kernel/branches/Kolibri-acpi/drivers/usbhid/usbhid.asm
0,0 → 1,553
; standard driver stuff
format MS COFF
 
DEBUG = 1
 
; this is for DEBUGF macro from 'fdo.inc'
__DEBUG__ = 1
__DEBUG_LEVEL__ = 1
 
include '../proc32.inc'
include '../imports.inc'
include '../fdo.inc'
include '../../struct.inc'
 
public START
public version
 
; Compile-time settings.
; If set, the code will dump all descriptors as they are read to the debug board.
USB_DUMP_DESCRIPTORS = 1
; If set, the code will dump any unclaimed input to the debug board.
HID_DUMP_UNCLAIMED = 1
 
; USB constants
DEVICE_DESCR_TYPE = 1
CONFIG_DESCR_TYPE = 2
STRING_DESCR_TYPE = 3
INTERFACE_DESCR_TYPE = 4
ENDPOINT_DESCR_TYPE = 5
DEVICE_QUALIFIER_DESCR_TYPE = 6
 
CONTROL_PIPE = 0
ISOCHRONOUS_PIPE = 1
BULK_PIPE = 2
INTERRUPT_PIPE = 3
 
; USB HID constants
HID_DESCR_TYPE = 21h
REPORT_DESCR_TYPE = 22h
PHYSICAL_DESCR_TYPE = 23h
 
; USB structures
struct config_descr
bLength db ?
bDescriptorType db ?
wTotalLength dw ?
bNumInterfaces db ?
bConfigurationValue db ?
iConfiguration db ?
bmAttributes db ?
bMaxPower db ?
ends
 
struct interface_descr
bLength db ?
bDescriptorType db ?
bInterfaceNumber db ?
bAlternateSetting db ?
bNumEndpoints db ?
bInterfaceClass db ?
bInterfaceSubClass db ?
bInterfaceProtocol db ?
iInterface db ?
ends
 
struct endpoint_descr
bLength db ?
bDescriptorType db ?
bEndpointAddress db ?
bmAttributes db ?
wMaxPacketSize dw ?
bInterval db ?
ends
 
; USB HID structures
struct hid_descr
bLength db ?
bDescriptorType db ?
bcdHID dw ?
bCountryCode db ?
bNumDescriptors db ?
base_sizeof rb 0
; now two fields are repeated .bNumDescriptors times:
subDescriptorType db ?
subDescriptorLength dw ?
ends
 
; Include macro for parsing report descriptors/data.
macro workers_globals
{}
include 'report.inc'
 
; Driver data for all devices
struct usb_device_data
hid hid_data ; data of HID layer
epdescr dd ? ; endpoint descriptor
hiddescr dd ? ; HID descriptor
interface_number dd ? ; copy of interface_descr.bInterfaceNumber
configpipe dd ? ; config pipe handle
intpipe dd ? ; interrupt pipe handle
input_transfer_size dd ? ; input transfer size
input_buffer dd ? ; buffer for input transfers
control rb 8 ; control packet to device
ends
 
section '.flat' code readable align 16
; The start procedure.
proc START
virtual at esp
dd ? ; return address
.reason dd ?
end virtual
; 1. Test whether the procedure is called with the argument DRV_ENTRY.
; If not, return 0.
xor eax, eax ; initialize return value
cmp [.reason], 1 ; compare the argument
jnz .nothing
; 2. Register self as a USB driver.
; The name is my_driver = 'usbhid'; IOCTL interface is not supported;
; usb_functions is an offset of a structure with callback functions.
stdcall RegUSBDriver, my_driver, eax, usb_functions
; 3. Return the returned value of RegUSBDriver.
.nothing:
ret 4
endp
 
; This procedure is called when new HID device is detected.
; It initializes the device.
proc AddDevice
push ebx esi edi ; save used registers to be stdcall
virtual at esp
rd 3 ; saved registers
dd ? ; return address
.config_pipe dd ?
.config_descr dd ?
.interface dd ?
end virtual
DEBUGF 1,'K : USB HID device detected\n'
; 1. Allocate memory for device data.
movi eax, sizeof.usb_device_data
call Kmalloc
test eax, eax
jnz @f
mov esi, nomemory_msg
call SysMsgBoardStr
jmp .return0
@@:
; zero-initialize it
mov edi, eax
xchg eax, ebx
xor eax, eax
movi ecx, sizeof.usb_device_data / 4
rep stosd
mov edx, [.interface]
; HID devices use one IN interrupt endpoint for polling the device
; and an optional OUT interrupt endpoint. We do not use the later,
; but must locate the first. Look for the IN interrupt endpoint.
; Also, look for the HID descriptor; according to HID spec, it must be
; located before endpoint descriptors.
; 2. Get the upper bound of all descriptors' data.
mov eax, [.config_descr]
movzx ecx, [eax+config_descr.wTotalLength]
add eax, ecx
; 3. Loop over all descriptors until
; either end-of-data reached - this is fail
; or interface descriptor found - this is fail, all further data
; correspond to that interface
; or endpoint descriptor for IN endpoint is found
; (HID descriptor must be located before the endpoint descriptor).
; 3a. Loop start: edx points to the interface descriptor.
.lookep:
; 3b. Get next descriptor.
movzx ecx, byte [edx] ; the first byte of all descriptors is length
test ecx, ecx
jz .cfgerror
add edx, ecx
; 3c. Check that at least two bytes are readable. The opposite is an error.
inc edx
cmp edx, eax
jae .cfgerror
dec edx
; 3d. Check that this descriptor is not interface descriptor. The opposite is
; an error.
cmp [edx+endpoint_descr.bDescriptorType], INTERFACE_DESCR_TYPE
jz .cfgerror
; 3e. For HID descriptor, proceed to 4.
; For endpoint descriptor, go to 5.
; For other descriptors, continue the loop.
; Note: bDescriptorType is in the same place in all descriptors.
cmp [edx+endpoint_descr.bDescriptorType], ENDPOINT_DESCR_TYPE
jz .foundep
cmp [edx+endpoint_descr.bDescriptorType], HID_DESCR_TYPE
jnz .lookep
; 4a. Check that the descriptor contains all required data and all data are
; readable. The opposite is an error.
movzx ecx, [edx+hid_descr.bLength]
cmp ecx, hid_descr.base_sizeof + 3
jb .cfgerror
add ecx, edx
cmp ecx, eax
ja .cfgerror
; 4b. Store the pointer in usb_device_data structure for further references.
mov [ebx+usb_device_data.hiddescr], edx
; 4c. Continue the loop.
jmp .lookep
.foundep:
; 5a. Check that the descriptor contains all required data and all data are
; readable. The opposite is an error.
cmp byte [edx+endpoint_descr.bLength], sizeof.endpoint_descr
jb .cfgerror
lea ecx, [edx+sizeof.endpoint_descr]
cmp ecx, eax
jbe @f
; 6. An error occured during processing endpoint descriptor.
.cfgerror:
; 6a. Print a message.
mov esi, invalid_config_descr_msg
call SysMsgBoardStr
; 6b. Free memory allocated for device data.
.free:
xchg eax, ebx
call Kfree
.return0:
; 6c. Return an error.
xor eax, eax
.nothing:
pop edi esi ebx ; restore used registers to be stdcall
ret 12
@@:
; 5b. If this is not IN interrupt endpoint, ignore it and continue the loop.
test [edx+endpoint_descr.bEndpointAddress], 80h
jz .lookep
mov cl, [edx+endpoint_descr.bmAttributes]
and cl, 3
cmp cl, INTERRUPT_PIPE
jnz .lookep
; 5c. Store the pointer in usb_device_data structure for futher references.
mov [ebx+usb_device_data.epdescr], edx
; 5d. Check that HID descriptor was found. If not, go to 6.
cmp [ebx+usb_device_data.hiddescr], 0
jz .cfgerror
.descriptors_found:
; 6. Configuration descriptor seems to be ok.
; Send SET_IDLE command disabling auto-repeat feature (it is quite useless)
; and continue configuring in SET_IDLE callback.
lea edx, [ebx+usb_device_data.control]
mov eax, [.interface]
mov dword [edx], 21h + \ ; Class-specific request to Interface
(0Ah shl 8) + \ ; SET_IDLE
(0 shl 16) + \ ; apply to all input reports
(0 shl 24) ; disable auto-repeat
movzx eax, [eax+interface_descr.bInterfaceNumber]
mov [ebx+usb_device_data.interface_number], eax
mov [edx+4], eax ; set interface number, zero length
mov eax, [.config_pipe]
mov [ebx+usb_device_data.configpipe], eax
xor ecx, ecx
stdcall USBControlTransferAsync, eax, edx, ecx, ecx, idle_set, ebx, ecx
; 7. Return pointer to usb_device_data.
xchg eax, ebx
jmp .nothing
endp
 
; This procedure is called by USB stack when SET_IDLE request initiated by
; AddDevice is completed, either successfully or unsuccessfully.
proc idle_set
push ebx esi ; save used registers to be stdcall
virtual at esp
rd 2 ; saved registers
dd ? ; return address
.pipe dd ?
.status dd ?
.buffer dd ?
.length dd ?
.calldata dd ?
end virtual
; Ignore status. Support for SET_IDLE is optional, so the device is free to
; STALL the request; config pipe should remain functional without explicit cleanup.
mov ebx, [.calldata]
; 1. HID descriptor contains length of Report descriptor. Parse it.
mov esi, [ebx+usb_device_data.hiddescr]
movzx ecx, [esi+hid_descr.bNumDescriptors]
lea eax, [hid_descr.base_sizeof+ecx*3]
cmp eax, 100h
jae .cfgerror
cmp al, [esi+hid_descr.bLength]
jb .cfgerror
.look_report:
dec ecx
js .cfgerror
cmp [esi+hid_descr.subDescriptorType], REPORT_DESCR_TYPE
jz .found_report
add esi, 3
jmp .look_report
.cfgerror:
mov esi, invalid_config_descr_msg
.abort_with_msg:
call SysMsgBoardStr
jmp .nothing
.found_report:
; 2. Send request for the Report descriptor.
; 2a. Allocate memory.
movzx eax, [esi+hid_descr.subDescriptorLength]
test eax, eax
jz .cfgerror
push eax
call Kmalloc
pop ecx
; If failed, say a message and stop initialization.
mov esi, nomemory_msg
test eax, eax
jz .abort_with_msg
; 2b. Submit the request.
xchg eax, esi
lea edx, [ebx+usb_device_data.control]
mov eax, [ebx+usb_device_data.interface_number]
mov dword [edx], 81h + \ ; Standard request to Interface
(6 shl 8) + \ ; GET_DESCRIPTOR
(0 shl 16) + \ ; descriptor index: there is only one report descriptor
(REPORT_DESCR_TYPE shl 24); descriptor type
mov [edx+4], ax ; Interface number
mov [edx+6], cx ; descriptor length
stdcall USBControlTransferAsync, [ebx+usb_device_data.configpipe], \
edx, esi, ecx, got_report, ebx, 0
; 2c. If failed, free the buffer and stop initialization.
test eax, eax
jnz .nothing
xchg eax, esi
call Kfree
.nothing:
pop esi ebx ; restore used registers to be stdcall
ret 20
endp
 
; This procedure is called by USB stack when the report descriptor queried
; by idle_set is completed, either successfully or unsuccessfully.
proc got_report stdcall uses ebx esi edi, pipe, status, buffer, length, calldata
locals
parse_descr_locals
if ~HID_DUMP_UNCLAIMED
has_driver db ?
rb 3
end if
endl
; 1. Check the status; if the request has failed, say something to the debug board
; and stop initialization.
cmp [status], 0
jnz .generic_fail
; 2. Subtract size of setup packet from the total length;
; the rest is length of the descriptor, and it must be nonzero.
sub [length], 8
ja .has_something
.generic_fail:
push esi
mov esi, reportfail
call SysMsgBoardStr
pop esi
jmp .exit
.has_something:
; 3. Process descriptor.
; 3a. Dump it to the debug board, if enabled in compile-time setting.
if USB_DUMP_DESCRIPTORS
mov eax, [buffer]
mov ecx, [length]
DEBUGF 1,'K : report descriptor:'
@@:
DEBUGF 1,' %x',[eax]:2
inc eax
dec ecx
jnz @b
DEBUGF 1,'\n'
end if
; 3b. Call the HID layer.
parse_descr
cmp [report_ok], 0
jz got_report.exit
mov ebx, [calldata]
postprocess_descr
; 4. Stop initialization if no driver is assigned.
if ~HID_DUMP_UNCLAIMED
cmp [has_driver], 0
jz got_report.exit
end if
; 5. Open interrupt IN pipe. If failed, stop initialization.
mov edx, [ebx+usb_device_data.epdescr]
movzx ecx, [edx+endpoint_descr.bEndpointAddress]
movzx eax, [edx+endpoint_descr.bInterval]
movzx edx, [edx+endpoint_descr.wMaxPacketSize]
stdcall USBOpenPipe, [ebx+usb_device_data.configpipe], ecx, edx, INTERRUPT_PIPE, eax
test eax, eax
jz got_report.exit
mov [ebx+usb_device_data.intpipe], eax
; 6. Initialize buffer for input packet.
; 6a. Find the length of input packet.
; This is the maximal length of all input reports.
mov edx, [ebx+usb_device_data.hid.input.first_report]
xor eax, eax
.find_input_size:
test edx, edx
jz .found_input_size
cmp eax, [edx+report.size]
jae @f
mov eax, [edx+report.size]
@@:
mov edx, [edx+report.next]
jmp .find_input_size
.found_input_size:
; report.size is in bits, transform it to bytes
add eax, 7
shr eax, 3
; if reports are numbered, the first byte is report ID, include it
cmp [ebx+usb_device_data.hid.input.numbered], 0
jz @f
inc eax
@@:
mov [ebx+usb_device_data.input_transfer_size], eax
; 6b. Allocate memory for input packet: dword-align and add additional dword
; for extract_field_value.
add eax, 4+3
and eax, not 3
call Kmalloc
test eax, eax
jnz @f
mov esi, nomemory_msg
call SysMsgBoardStr
jmp got_report.exit
@@:
mov [ebx+usb_device_data.input_buffer], eax
; 7. Submit a request for input packet and wait for input.
call ask_for_input
got_report.exit:
mov eax, [buffer]
call Kfree
ret
endp
 
; Helper procedure for got_report and got_input.
; Submits a request for the next input packet.
proc ask_for_input
; just call USBNormalTransferAsync with correct parameters,
; allow short packets
stdcall USBNormalTransferAsync, \
[ebx+usb_device_data.intpipe], \
[ebx+usb_device_data.input_buffer], \
[ebx+usb_device_data.input_transfer_size], \
got_input, ebx, \
1
ret
endp
 
; This procedure is called by USB stack when a HID device responds with input
; data packet.
proc got_input stdcall uses ebx esi edi, pipe, status, buffer, length, calldata
locals
parse_input_locals
endl
; 1. Validate parameters: fail on error, ignore zero-length transfers.
mov ebx, [calldata]
cmp [status], 0
jnz .fail
cmp [length], 0
jz .done
; 2. Get pointer to report in esi.
; 2a. If there are no report IDs, use hid.input.data.
mov eax, [buffer]
mov esi, [ebx+usb_device_data.hid.input.data]
cmp [ebx+usb_device_data.hid.input.numbered], 0
jz .report_found
; 2b. Otherwise, the first byte of report is report ID;
; locate the report by its ID, advance buffer+length to one byte.
movzx eax, byte [eax]
mov esi, [esi+eax*4]
inc [buffer]
dec [length]
.report_found:
; 3. Validate: ignore transfers with unregistered report IDs
; and transfers which are too short for the corresponding report.
test esi, esi
jz .done
mov eax, [esi+report.size]
add eax, 7
shr eax, 3
cmp eax, [length]
ja .done
; 4. Pass everything to HID layer.
parse_input
.done:
; 5. Query the next input.
mov ebx, [calldata]
call ask_for_input
.nothing:
ret
.fail:
mov esi, transfer_error_msg
call SysMsgBoardStr
jmp .nothing
endp
 
; This function is called by the USB subsystem when a device is disconnected.
proc DeviceDisconnected
push ebx esi edi ; save used registers to be stdcall
virtual at esp
rd 3 ; saved registers
dd ? ; return address
.device_data dd ?
end virtual
; 1. Say a message.
mov ebx, [.device_data]
mov esi, disconnectmsg
stdcall SysMsgBoardStr
; 2. Ask HID layer to release all HID-related resources.
hid_cleanup
; 3. Free the device data.
xchg eax, ebx
call Kfree
; 4. Return.
.nothing:
pop edi esi ebx ; restore used registers to be stdcall
ret 4 ; purge one dword argument to be stdcall
endp
 
include 'sort.inc'
include 'unclaimed.inc'
include 'mouse.inc'
include 'keyboard.inc'
 
; strings
my_driver db 'usbhid',0
nomemory_msg db 'K : no memory',13,10,0
invalid_config_descr_msg db 'K : invalid config descriptor',13,10,0
reportfail db 'K : failed to read report descriptor',13,10,0
transfer_error_msg db 'K : USB transfer error, disabling HID device',13,10,0
disconnectmsg db 'K : USB HID device disconnected',13,10,0
invalid_report_msg db 'K : report descriptor is invalid',13,10,0
delimiter_note db 'K : note: alternate usage ignored',13,10,0
 
; Exported variable: kernel API version.
align 4
version dd 50005h
; Structure with callback functions.
usb_functions:
dd 12
dd AddDevice
dd DeviceDisconnected
 
; for DEBUGF macro
include_debug_strings
 
; Workers data
workers_globals
 
; for uninitialized data
;section '.data' data readable writable align 16
/kernel/branches/Kolibri-acpi/drivers/usbhid
Property changes:
Added: tsvn:logminsize
+5
\ No newline at end of property
/kernel/branches/Kolibri-acpi/drivers/vidrdc.asm
0,0 → 1,438
; Stub of videodriver for RDC Semiconductor Co. M2010/M2012 videocards (controller names: R3306/R3308).
; It is used in SoC produced by DMP Electronics Inc.:
; Vortex86MX (contains RDC M2010 graphics card, appears in eBox-3300MX)
; Vortex86MX+ (contains RDC M2012 graphics card, appears in eBox-3310MX)
; Link to manufacturers websites -
; RDC Semiconductor Co.: http://www.rdc.com.tw
; DM&P Electronics Inc.: http://www.dmp.com.tw and http://www.compactpc.com.tw
; Code stolen from vidintel.asm driver (c) by CleverMouse and adapted for RDC.
 
; When the start procedure gets control,
; it tries to detect preferred resolution,
; sets the detected resolution assuming 32-bpp VESA mode and exits
; (without registering a service).
; Detection can be overloaded with compile-time settings
; use_predefined_mode/predefined_width/predefined_height.
 
; set predefined resolution here
use_predefined_mode = 0;1
predefined_width = 0;1366
predefined_height = 0;768
 
; standard driver stuff
format MS COFF
 
DEBUG = 1
 
include 'proc32.inc'
include 'imports.inc'
 
public START
public version
 
section '.flat' code readable align 16
; the start procedure (see the description above)
START:
; 1. Detect device. Abort if not found.
push esi
call DetectDevice
test esi, esi
jz .return0
 
;{START}yogev_ezra: temporary exit after detection
pusha
mov esi, exitmsg
call SysMsgBoardStr
popa
jmp .return0
;{END}yogev_ezra: temporary exit after detection
; 2. Detect optimal mode unless the mode is given explicitly. Abort if failed.
if use_predefined_mode = 0
call DetectMode
end if
cmp [width], 0
jz .return0_cleanup
; 3. Set the detected mode.
call SetMode
; 4. Cleanup and return.
.return0_cleanup:
stdcall FreeKernelSpace, esi
.return0:
pop esi
xor eax, eax
ret 4
 
; check that there is RDC videocard
; if so, map MMIO registers and set internal variables
; esi points to MMIO block; NULL means no device
DetectDevice:
; 1. Sanity check: check that we are dealing with RDC videocard.
; Integrated video device for RDC is always at PCI:0:13:0 (bus:dev:fn=0:0d:0)
xor esi, esi ; initialize return value to NULL
; 1a. Get PCI VendorID and DeviceID.
push esi ; in: reg=0 (register) -> register 00 means return DeviceID (bits 16-31) + VendorID (bits 0-15)
push 68h ; in: devfn=13:0 | device:5bit (0Dh = 1101) + func:3bit (0 = 000) -> total:1byte (1101000b = 68h)
push esi ; in: bus=0
call PciRead32
; 1b. loword(eax) = ax = VendorID, hiword(eax) = DeviceID.
; Test whether we have RDC Semiconductor Co. chipset.
cmp ax, 17F3h ;VendorID 0x17F3, 'RDC Semiconductor Co.'
jnz .return
; 1c. Say hi including DeviceID.
shr eax, 10h ; now, ax = HIWORD(eax) = PCI DeviceID
push edi
pusha
mov edi, pciid_text ; edi='0000'
call WriteWord
mov esi, hellomsg
call SysMsgBoardStr
popa
; 1d. Test whether we know this DeviceID.
; If this is the case, remember the position of the device in line of RDC cards;
; this knowledge will be useful later.
; Tested on devices with id: 17F3:2010, 17F3:2012.
mov ecx, pciids_num
mov edi, pciids
repnz scasw
pop edi
jnz .return_unknown_pciid
sub ecx, pciids_num - 1
neg ecx
mov [deviceType], ecx
; 1e. Continue saying hi with positive intonation.
pusha
mov esi, knownmsg
call SysMsgBoardStr
popa
; 2. Prepare MMIO region to control the card.
; 2a. Read MMIO physical address from PCI config space.
; According to RDC M2010/M2012 registers manual, their memory-mapped I/O space is located at Base address #1
push 14h ; in: reg=14h (register) -> register 14h means Base address #1 (BAR1) in PCI configuration space
push 68h ; in: devfn=13:0 | device:5bit (0Dh = 1101) + func:3bit (0 = 000) -> total:1byte (1101000b = 68h)
push esi ; in: bus=0
call PciRead32
; 2b. Mask out PCI region type, lower 4 bits.
and al, not 0xF
; 2c. Create virtual mapping of the physical memory.
push 1Bh
push 100000h
push eax
call MapIoMem
; 3. Return.
xchg esi, eax
.return:
ret
; 1f. If we do not know DeviceID, continue saying hi with negative intonation.
.return_unknown_pciid:
pusha
mov esi, unknownmsg
call SysMsgBoardStr
popa
ret
 
; Convert word in ax to hexadecimal text in edi, advance edi.
WriteWord:
; 1. Convert high byte.
push eax
mov al, ah
call WriteByte
pop eax
; 2. Convert low byte.
; Fall through to WriteByte; ret from WriteByte is ret from WriteWord too.
 
; Convert byte in al to hexadecimal text in edi, advance edi.
WriteByte:
; 1. Convert high nibble.
push eax
shr al, 4
call WriteNibble
pop eax
; 2. Convert low nibble.
and al, 0xF
; Fall through to WriteNibble; ret from WriteNibble is ret from WriteByte too.
 
; Convert nibble in al to hexadecimal text in edi, advance edi.
WriteNibble:
; Obvious, isn't it?
cmp al, 10
sbb al, 69h
das
stosb ; This instruction uses EDI implicitly
ret
 
if use_predefined_mode = 0
; detect resolution of the flat panel
DetectMode:
push esi edi
; 1. Get the location of block of GMBUS* registers.
; Starting with Ironlake, GMBUS* registers were moved.
add esi, 5100h
cmp [deviceType], pciids_num ;ironlake_start
jb @f
add esi, 0xC0000
@@:
; 2. Initialize GMBUS engine.
mov edi, edid
mov ecx, 0x10000
@@:
test byte [esi+8+1], 80h
loopnz @b
jnz .fail
mov dword [esi], 3
test byte [esi+8+1], 4
jz .noreset
call ResetGMBus
jnz .fail
.noreset:
; 3. Send read command.
and dword [esi+20h], 0
mov dword [esi+4], 4E8000A1h
; 4. Wait for data, writing to the buffer as data arrive.
.getdata:
mov ecx, 0x10000
@@:
test byte [esi+8+1], 8
loopz @b
test byte [esi+8+1], 4
jz .dataok
call ResetGMBus
jmp .fail
.dataok:
mov eax, [esi+0Ch]
stosd
cmp edi, edid+80h
jb .getdata
; 5. Wait for bus idle.
mov ecx, 0x10000
@@:
test byte [esi+8+1], 2
loopnz @b
; 6. We got EDID; dump it if DEBUG.
if DEBUG
pusha
xor ecx, ecx
mov esi, edid
mov edi, edid_text
.dumploop:
lodsb
call WriteByte
mov al, ' '
stosb
inc cl
test cl, 15
jnz @f
mov byte [edi-1], 13
mov al, 10
stosb
@@:
test cl, cl
jns .dumploop
mov esi, edidmsg
call SysMsgBoardStr
popa
end if
; 7. Test whether EDID is good.
; 7a. Signature: 00 FF FF FF FF FF FF 00.
mov esi, edid
cmp dword [esi], 0xFFFFFF00
jnz .fail
cmp dword [esi+4], 0x00FFFFFF
jnz .fail
; 7b. Checksum must be zero.
xor edx, edx
mov ecx, 80h
@@:
lodsb
add dl, al
loop @b
jnz .fail
; 8. Get width and height from EDID.
xor eax, eax
mov ah, [esi-80h+3Ah]
shr ah, 4
mov al, [esi-80h+38h]
mov [width], eax
mov ah, [esi-80h+3Dh]
shr ah, 4
mov al, [esi-80h+3Bh]
mov [height], eax
; 9. Return.
.fail:
pop edi esi
ret
 
; reset bus, clear all errors
ResetGMBus:
; look into the PRM
mov dword [esi+4], 80000000h
mov dword [esi+4], 0
mov ecx, 0x10000
@@:
test byte [esi+8+1], 2
loopnz @b
ret
end if
 
; set resolution [width]*[height]
SetMode:
; 1. Program the registers of videocard.
; look into the PRM
cli
; or byte [esi+7000Ah], 0Ch ; PIPEACONF: disable Display+Cursor Planes
; or byte [esi+7100Ah], 0Ch ; PIPEBCONF: disable Display+Cursor Planes
xor eax, eax
xor edx, edx
cmp [deviceType], pciids_num ;i965_start
jb @f
mov dl, 9Ch - 84h
@@:
; or byte [esi+71403h], 80h ; VGACNTRL: VGA Display Disable
and byte [esi+70080h], not 27h ; CURACNTR: disable cursor A
mov dword [esi+70084h], eax ; CURABASE: force write to CURA* regs
and byte [esi+700C0h], not 27h ; CURBCNTR: disable cursor B
mov dword [esi+700C4h], eax ; CURBBASE: force write to CURB* regs
and byte [esi+70183h], not 80h ; DSPACNTR: disable Primary A Plane
mov dword [esi+edx+70184h], eax ; DSPALINOFF/DSPASURF: force write to DSPA* regs
and byte [esi+71183h], not 80h ; DSPBCNTR: disable Primary B Plane
mov dword [esi+edx+71184h], eax ; DSPBLINOFF/DSPBSURF: force write to DSPB* regs
if 1
cmp [deviceType], pciids_num ;ironlake_start
jae .disable_pipes
mov edx, 10000h
or byte [esi+70024h], 2 ; PIPEASTAT: clear VBLANK status
or byte [esi+71024h], 2 ; PIPEBSTAT: clear VBLANK status
.wait_vblank_preironlake1:
mov ecx, 1000h
loop $
test byte [esi+7000Bh], 80h ; PIPEACONF: pipe A active?
jz @f
test byte [esi+70024h], 2 ; PIPEASTAT: got VBLANK?
jz .wait_vblank_preironlake2
@@:
test byte [esi+7100Bh], 80h ; PIPEBCONF: pipe B active?
jz .disable_pipes
test byte [esi+71024h], 2 ; PIPEBSTAT: got VBLANK?
jnz .disable_pipes
.wait_vblank_preironlake2:
dec edx
jnz .wait_vblank_preironlake1
jmp .not_disabled
.disable_pipes:
end if
and byte [esi+7000Bh], not 80h ; PIPEACONF: disable pipe
and byte [esi+7100Bh], not 80h ; PIPEBCONF: disable pipe
cmp [deviceType], pciids_num ;gen4_start
jb .wait_watching_scanline
; g45 and later: use special flag from PIPE*CONF
mov edx, 10000h
@@:
mov ecx, 1000h
loop $
test byte [esi+7000Bh], 40h ; PIPEACONF: wait until pipe disabled
jz @f
dec edx
jnz @b
jmp .not_disabled
@@:
test byte [esi+7100Bh], 40h ; PIPEBCONF: wait until pipe disabled
jz .disabled
mov ecx, 1000h
loop $
dec edx
jnz @b
jmp .not_disabled
; pineview and before: wait while scanline still changes
.wait_watching_scanline:
mov edx, 1000h
.dis1:
push dword [esi+71000h]
push dword [esi+70000h]
mov ecx, 10000h
loop $
pop eax
xor eax, [esi+70000h]
and eax, 1FFFh
pop eax
jnz .notdis1
xor eax, [esi+71000h]
and eax, 1FFFh
jz .disabled
.notdis1:
dec edx
jnz .dis1
.not_disabled:
sti
jmp .return
.disabled:
lea eax, [esi+61183h]
cmp [deviceType], pciids_num ;ironlake_start
jb @f
add eax, 0xE0000 - 0x60000
@@:
lea edx, [esi+60000h]
test byte [eax], 40h
jz @f
add edx, 1000h
@@:
mov eax, [width]
dec eax
shl eax, 16
mov ax, word [height]
dec eax
mov dword [edx+1Ch], eax ; PIPEASRC: set source image size
ror eax, 16
mov dword [edx+10190h], eax ; for old cards
mov ecx, [width]
add ecx, 15
and ecx, not 15
shl ecx, 2
mov dword [edx+10188h], ecx ; DSPASTRIDE: set scanline length
mov dword [edx+10184h], 0 ; DSPALINOFF: force write to DSPA* registers
and byte [esi+61233h], not 80h ; PFIT_CONTROL: disable panel fitting
or byte [edx+1000Bh], 80h ; PIPEACONF: enable pipe
; and byte [edx+1000Ah], not 0Ch ; PIPEACONF: enable Display+Cursor Planes
or byte [edx+10183h], 80h ; DSPACNTR: enable Display Plane A
sti
; 2. Notify the kernel that resolution has changed.
call GetDisplay
mov edx, [width]
mov dword [eax+8], edx
mov edx, [height]
mov dword [eax+0Ch], edx
mov [eax+18h], ecx
mov eax, [width]
dec eax
dec edx
call SetScreen
.return:
ret
 
align 4
hellomsg db 'RDC videocard detected, PciId=17F3:' ;VendorID 0x17F3, 'RDC Semiconductor Co.'
pciid_text db '0000'
db ', which is ', 0
knownmsg db 'known',13,10,0
unknownmsg db 'unknown',13,10,0
exitmsg db 'Card detected successfully, exiting driver...',13,10,0
 
if DEBUG
edidmsg db 'EDID successfully read:',13,10
edid_text rb 8*(16*3+1)
db 0
end if
 
version:
dd 0x50005
 
width dd predefined_width
height dd predefined_height
 
pciids:
dw 0x2010 ; M2010 - appears in eBox-3300MX (Vortex86MX SoC)
dw 0x2012 ; M2012 - appears in eBox-3310MX (Vortex86MX+ SoC)
pciids_num = ($ - pciids) / 2
 
align 4
deviceType dd ?
edid rb 0x80
/kernel/branches/Kolibri-acpi/fs/fs-sp.inc
File deleted
/kernel/branches/Kolibri-acpi/fs/fs-et.inc
File deleted
\ No newline at end of file
/kernel/branches/Kolibri-acpi/fs/fat12.inc
File deleted
Property changes:
Deleted: svn:keywords
-Rev
\ No newline at end of property
/kernel/branches/Kolibri-acpi/fs/fat32.inc
File deleted
Property changes:
Deleted: svn:keywords
-Rev
\ No newline at end of property
/kernel/branches/Kolibri-acpi/fs/fs.inc
File deleted
Property changes:
Deleted: svn:keywords
-Rev
\ No newline at end of property
/kernel/branches/Kolibri-acpi/fs/part_set.inc
File deleted
Property changes:
Deleted: svn:keywords
-Rev
\ No newline at end of property
/kernel/branches/Kolibri-acpi/fs/fat.inc
0,0 → 1,3705
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; FAT32.INC ;;
;; ;;
;; FAT functions for KolibriOS ;;
;; ;;
;; Copyright 2002 Paolo Minazzi, paolo.minazzi@inwind.it ;;
;; ;;
;; See file COPYING for details ;;
;; 04.02.2007 LFN create folder - diamond ;;
;; 08.10.2006 LFN delete file/folder - diamond ;;
;; 20.08.2006 LFN set file size (truncate/extend) - diamond ;;
;; 17.08.2006 LFN write/append to file - diamond ;;
;; 23.06.2006 LFN start application - diamond ;;
;; 15.06.2006 LFN get/set file/folder info - diamond ;;
;; 27.05.2006 LFN create/rewrite file - diamond ;;
;; 04.05.2006 LFN read folder - diamond ;;
;; 29.04.2006 Elimination of hangup after the ;;
;; expiration hd_wait_timeout - Mario79 ;;
;; 23.04.2006 LFN read file - diamond ;;
;; 28.01.2006 find all Fat16/32 partition in all input point ;;
;; to MBR, see file part_set.inc - Mario79 ;;
;; 15.01.2005 get file size/attr/date, file_append - ATV ;;
;; 04.12.2004 skip volume label, file delete bug fixed - ATV ;;
;; 29.11.2004 get_free_FAT changed, append dir bug fixed - ATV ;;
;; 23.11.2004 don't allow overwrite dir with file - ATV ;;
;; 18.11.2004 get_disk_info and more error codes - ATV ;;
;; 17.11.2004 set_FAT/get_FAT and disk cache rewritten - ATV ;;
;; 10.11.2004 removedir clear whole directory structure - ATV ;;
;; 08.11.2004 rename - ATV ;;
;; 30.10.2004 file_read return also dirsize in bytes - ATV ;;
;; 20.10.2004 Makedir/Removedir - ATV ;;
;; 14.10.2004 Partition chain/Fat16 - ATV (thanks drh3xx) ;;
;; 06.9.2004 Fix free space by Mario79 added - MH ;;
;; 24.5.2004 Write back buffer for File_write -VT ;;
;; 20.5.2004 File_read function to work with syscall 58 - VT ;;
;; 30.3.2004 Error parameters at function return - VT ;;
;; 01.5.2002 Bugfix in device write - VT ;;
;; 20.5.2002 Hd status check - VT ;;
;; 29.6.2002 Improved fat32 verification - VT ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 4273 $
 
 
cache_max equ 1919 ; max. is 1919*512+0x610000=0x6ffe00
 
PUSHAD_EAX equ [esp+28]
PUSHAD_ECX equ [esp+24]
PUSHAD_EDX equ [esp+20]
PUSHAD_EBX equ [esp+16]
PUSHAD_EBP equ [esp+8]
PUSHAD_ESI equ [esp+4]
PUSHAD_EDI equ [esp+0]
 
; Internal data for every FAT partition.
struct FAT PARTITION
fs_type db ?
fat16_root db 0 ; flag for fat16 rootdir
fat_change db 0 ; 1=fat has changed
db ? ; alignment
Lock MUTEX ? ; currently operations with one partition
; can not be executed in parallel since the
; legacy code is not ready; this mutex guards
; all operations
SECTORS_PER_FAT dd 0x1f3a
NUMBER_OF_FATS dd 0x2
SECTORS_PER_CLUSTER dd 0x8
BYTES_PER_SECTOR dd 0x200 ; Note: if BPS <> 512 need lots of changes
ROOT_CLUSTER dd 2 ; first rootdir cluster
FAT_START dd 0 ; start of fat table
ROOT_START dd 0 ; start of rootdir (only fat16)
ROOT_SECTORS dd 0 ; count of rootdir sectors (only fat16)
DATA_START dd 0 ; start of data area (=first cluster 2)
LAST_CLUSTER dd 0 ; last availabe cluster
ADR_FSINFO dd 0 ; used only by fat32
 
fatRESERVED dd 0x0FFFFFF6
fatBAD dd 0x0FFFFFF7
fatEND dd 0x0FFFFFF8
fatMASK dd 0x0FFFFFFF
 
fatStartScan dd 2
 
cluster_tmp dd 0 ; used by analyze_directory
; and analyze_directory_to_write
 
longname_sec1 dd 0 ; used by analyze_directory to save 2 previous
longname_sec2 dd 0 ; directory sectors for delete long filename
 
fat_in_cache dd -1
 
; For FAT16/FAT32, this points to 512-byte buffer for the current sector of FAT.
; For FAT12, the entire FAT structure is read
; and unpacked from 12bit per cluster to word per cluster.
;
; Note: work with unpacked copy of FAT12 means
; additional memory and additional code for packing/unpacking.
; I'm not sure that the economy justifies the cost, but anyway,
; there is how work was done before my edits, and I'm just keeping the principle.
fat_cache_ptr dd ?
fat12_unpacked_ptr dd ?
buffer rb 512
fsinfo_buffer rb 512
ends
 
uglobal
align 4
partition_count dd 0 ; partitions found by set_FAT32_variables
 
hd_error dd 0 ; set by wait_for_sector_buffer
hd_setup dd 0
hd_wait_timeout dd 0
 
cache_search_start dd 0 ; used by find_empty_slot
endg
 
uglobal
align 4
Sector512: ; label for dev_hdcd.inc
buffer:
times 512 db 0
endg
 
iglobal
align 4
fat_user_functions:
dd fat_free
dd (fat_user_functions_end - fat_user_functions - 4) / 4
dd fat_Read
dd fat_ReadFolder
dd fat_Rewrite
dd fat_Write
dd fat_SetFileEnd
dd fat_GetFileInfo
dd fat_SetFileInfo
dd 0
dd fat_Delete
dd fat_CreateFolder
fat_user_functions_end:
endg
 
; these labels are located before the main function to make
; most of jumps to these be short
fat_create_partition.free_return0:
mov eax, ebp
call free
pop ebp
fat_create_partition.return0:
xor eax, eax
ret
fat_create_partition:
; bootsector must have been successfully read
cmp dword [esp+4], 0
jnz .return0
; bootsector signature must be correct
cmp word [ebx+0x1fe], 0xaa55
jnz .return0
; sectors per cluster must be nonzero
cmp byte [ebx+0xd], 0
jz .return0
; bytes per sector must be 0x200
cmp word [ebx+0xb], 0x200
jnz .return0
; number of fats must be nonzero
cmp byte [ebx+0x10], 0
jz .return0
; The only reason to be invalid partition now is FAT12. Since the test for
; FAT size requires knowledge of some calculated values, which are also used
; in the normal operation, let's hope for the best and allocate data now; if
; it will prove wrong, just deallocate it.
movi eax, sizeof.FAT
call malloc
test eax, eax
jz .return0
mov ecx, dword [ebp+PARTITION.FirstSector]
mov dword [eax+FAT.FirstSector], ecx
mov ecx, dword [ebp+PARTITION.FirstSector+4]
mov dword [eax+FAT.FirstSector+4], ecx
mov ecx, dword [ebp+PARTITION.Length]
mov dword [eax+FAT.Length], ecx
mov ecx, dword [ebp+PARTITION.Length+4]
mov dword [eax+FAT.Length+4], ecx
mov ecx, [ebp+PARTITION.Disk]
mov [eax+FAT.Disk], ecx
mov [eax+FAT.FSUserFunctions], fat_user_functions
or [eax+FAT.fat_in_cache], -1
mov [eax+FAT.fat_change], 0
push ebp
mov ebp, eax
 
lea ecx, [ebp+FAT.Lock]
call mutex_init
 
movzx eax, word [ebx+0xe] ; sectors reserved
mov [ebp+FAT.FAT_START], eax
 
movzx eax, byte [ebx+0xd] ; sectors per cluster
mov [ebp+FAT.SECTORS_PER_CLUSTER], eax
 
movzx ecx, word [ebx+0xb] ; bytes per sector
mov [ebp+FAT.BYTES_PER_SECTOR], ecx
 
movzx eax, word [ebx+0x11] ; count of rootdir entries (=0 fat32)
shl eax, 5 ; mul 32
dec ecx
add eax, ecx ; round up if not equal count
inc ecx ; bytes per sector
xor edx, edx
div ecx
mov [ebp+FAT.ROOT_SECTORS], eax ; count of rootdir sectors
 
movzx eax, word [ebx+0x16] ; sectors per fat <65536
test eax, eax
jnz @f
mov eax, [ebx+0x24] ; sectors per fat
@@:
mov [ebp+FAT.SECTORS_PER_FAT], eax
 
movzx eax, byte [ebx+0x10] ; number of fats
mov [ebp+FAT.NUMBER_OF_FATS], eax
mul [ebp+FAT.SECTORS_PER_FAT]
test edx, edx
jnz .free_return0
add eax, [ebp+FAT.FAT_START]
jc .free_return0
mov [ebp+FAT.ROOT_START], eax ; rootdir = fat_start + fat_size * fat_count
add eax, [ebp+FAT.ROOT_SECTORS] ; rootdir sectors should be 0 on fat32
jc .free_return0
mov [ebp+FAT.DATA_START], eax ; data area = rootdir + rootdir_size
 
movzx eax, word [ebx+0x13] ; total sector count <65536
test eax, eax
jnz @f
mov eax, [ebx+0x20] ; total sector count
@@:
; total sector count must not exceed partition size
cmp dword [ebp+FAT.Length+4], 0
jnz @f
cmp eax, dword [ebp+FAT.Length]
ja .free_return0
@@:
mov dword [ebp+FAT.Length], eax
and dword [ebp+FAT.Length+4], 0
sub eax, [ebp+FAT.DATA_START] ; eax = count of data sectors
jc .free_return0
xor edx, edx
div [ebp+FAT.SECTORS_PER_CLUSTER]
inc eax
mov [ebp+FAT.LAST_CLUSTER], eax
dec eax ; cluster count
jz .free_return0
mov [ebp+FAT.fatStartScan], 2
 
; limits by Microsoft Hardware White Paper v1.03
cmp eax, 4085 ; 0xff5
jb .fat12
cmp eax, 65525 ; 0xfff5
jb .fat16
.fat32:
mov eax, [ebx+0x2c] ; rootdir cluster
mov [ebp+FAT.ROOT_CLUSTER], eax
movzx eax, word [ebx+0x30]
mov [ebp+FAT.ADR_FSINFO], eax
push ebx
add ebx, 512
call fs_read32_sys
test eax, eax
jnz @f
mov eax, [ebx+0x1ec]
cmp eax, -1
jz @f
mov [ebp+FAT.fatStartScan], eax
@@:
pop ebx
mov [ebp+FAT.fatRESERVED], 0x0FFFFFF6
mov [ebp+FAT.fatBAD], 0x0FFFFFF7
mov [ebp+FAT.fatEND], 0x0FFFFFF8
mov [ebp+FAT.fatMASK], 0x0FFFFFFF
mov al, 32
.fat_not_12_finalize:
mov [ebp+FAT.fs_type], al
; For FAT16 and FAT32, allocate 512 bytes for FAT cache.
mov eax, 512
call malloc
test eax, eax
jz .free_return0
mov [ebp+FAT.fat_cache_ptr], eax
mov eax, ebp
pop ebp
ret
.fat16:
and [ebp+FAT.ROOT_CLUSTER], 0
mov [ebp+FAT.fatRESERVED], 0x0000FFF6
mov [ebp+FAT.fatBAD], 0x0000FFF7
mov [ebp+FAT.fatEND], 0x0000FFF8
mov [ebp+FAT.fatMASK], 0x0000FFFF
mov al, 16
jmp .fat_not_12_finalize
.fat12:
and [ebp+FAT.ROOT_CLUSTER], 0
mov [ebp+FAT.fatRESERVED], 0xFF6
mov [ebp+FAT.fatBAD], 0xFF7
mov [ebp+FAT.fatEND], 0xFFF
mov [ebp+FAT.fatMASK], 0xFFF
mov al, 12
mov [ebp+FAT.fs_type], al
; For FAT12, allocate&read data for entire table:
; calculate A = ALIGN_UP(NUM_CLUSTERS, 8),
; calculatefatchain/restorefatchain will process A items,
; allocate ALIGN_UP(A*3/2, 512) bytes for FAT table plus A*2 bytes for unpacked data.
mov eax, [ebp+FAT.LAST_CLUSTER]
and eax, not 7
add eax, 8
mov edx, eax
lea eax, [eax*3]
add eax, 512*2-1
shr eax, 10
shl eax, 9
lea eax, [eax+edx*2]
call malloc
test eax, eax
jz .free_return0
; Read ALIGN_UP(NUM_CLUSTERS*3/2, 512) bytes.
; Note that this can be less than allocated, this is ok,
; overallocation simplifies calculatefatchain/restorefatchain.
push ebx
mov [ebp+FAT.fat_cache_ptr], eax
mov edx, [ebp+FAT.LAST_CLUSTER]
lea edx, [(edx+1)*3 + 512*2-1]
shr edx, 10
xchg eax, ebx
xor eax, eax
.read_fat:
push eax
add eax, [ebp+FAT.FAT_START]
call fs_read32_sys
test eax, eax
pop eax
jz @f
dbgstr 'Failed to read FAT table'
mov eax, [ebp+FAT.fat_cache_ptr]
call free
pop ebx
jmp .free_return0
@@:
add ebx, 512
inc eax
cmp eax, edx
jb .read_fat
mov [ebp+FAT.fat12_unpacked_ptr], ebx
call calculatefatchain
pop ebx
mov eax, ebp
pop ebp
ret
 
fat_free:
push eax
mov eax, [eax+FAT.fat_cache_ptr]
call free
pop eax
jmp free
 
calculatefatchain:
 
pushad
 
mov esi, [ebp+FAT.fat_cache_ptr]
mov edi, [ebp+FAT.fat12_unpacked_ptr]
 
mov edx, [ebp+FAT.LAST_CLUSTER]
and edx, not 7
lea edx, [edi+(edx+8)*2]
push edx
 
fcnew:
mov eax, dword [esi]
mov ebx, dword [esi+4]
mov ecx, dword [esi+8]
mov edx, ecx
shr edx, 4;8 ok
shr dx, 4;7 ok
xor ch, ch
shld ecx, ebx, 20;6 ok
shr cx, 4;5 ok
shld ebx, eax, 12
and ebx, 0x0fffffff;4 ok
shr bx, 4;3 ok
shl eax, 4
and eax, 0x0fffffff;2 ok
shr ax, 4;1 ok
mov dword [edi], eax
mov dword [edi+4], ebx
mov dword [edi+8], ecx
mov dword [edi+12], edx
add edi, 16
add esi, 12
 
cmp edi, [esp]
jnz fcnew
pop eax
 
popad
ret
 
 
restorefatchain: ; restore fat chain
 
pushad
 
mov esi, [ebp+FAT.fat12_unpacked_ptr]
mov edi, [ebp+FAT.fat_cache_ptr]
 
mov edx, [ebp+FAT.LAST_CLUSTER]
and edx, not 7
lea edx, [esi+(edx+8)*2]
 
fcnew2:
mov eax, dword [esi]
mov ebx, dword [esi+4]
shl ax, 4
shl eax, 4
shl bx, 4
shr ebx, 4
shrd eax, ebx, 8
shr ebx, 8
mov dword [edi], eax
mov word [edi+4], bx
add edi, 6
add esi, 8
 
cmp esi, edx
jb fcnew2
 
mov esi, [ebp+FAT.NUMBER_OF_FATS]
mov edx, [ebp+FAT.LAST_CLUSTER]
lea edx, [(edx+1)*3 + 512*2-1]
shr edx, 10
push [ebp+FAT.FAT_START]
 
.write_fats:
xor eax, eax
mov ebx, [ebp+FAT.fat_cache_ptr]
.loop1:
push eax
add eax, [esp+4]
call fs_write32_sys
test eax, eax
pop eax
jnz .fail
add ebx, 512
inc eax
cmp eax, edx
jb .loop1
pop eax
add eax, [ebp+FAT.SECTORS_PER_FAT]
push eax
dec esi
jnz .write_fats
pop eax
 
popad
ret
.fail:
dbgstr 'Failed to save FAT'
popad
ret
 
iglobal
label fat_legal_chars byte
; 0 = not allowed
; 1 = allowed only in long names
; 3 = allowed
times 32 db 0
; ! " # $ % & ' ( ) * + , - . /
db 1,3,0,3,3,3,3,3,3,3,0,1,1,3,3,0
; 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
db 3,3,3,3,3,3,3,3,3,3,0,1,0,1,0,0
; @ A B C D E F G H I J K L M N O
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; P Q R S T U V W X Y Z [ \ ] ^ _
db 3,3,3,3,3,3,3,3,3,3,3,1,0,1,3,3
; ` a b c d e f g h i j k l m n o
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; p q r s t u v w x y z { | } ~
db 3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0
endg
 
fat_name_is_legal:
; in: esi->(long) name
; out: CF set <=> legal
; destroys eax
push esi
xor eax, eax
@@:
lodsb
test al, al
jz .done
cmp al, 80h
jae .big
test [fat_legal_chars+eax], 1
jnz @b
.err:
pop esi
clc
ret
.big:
; 0x80-0xAF, 0xE0-0xEF
cmp al, 0xB0
jb @b
cmp al, 0xE0
jb .err
cmp al, 0xF0
jb @b
jmp .err
.done:
sub esi, [esp]
cmp esi, 257
pop esi
ret
 
fat_next_short_name:
; in: edi->8+3 name
; out: name corrected
; CF=1 <=> error
pushad
mov ecx, 8
mov al, '~'
std
push edi
add edi, 7
repnz scasb
pop edi
cld
jz .tilde
; tilde is not found, insert "~1" at end
add edi, 6
cmp word [edi], ' '
jnz .insert_tilde
@@:
dec edi
cmp byte [edi], ' '
jz @b
inc edi
.insert_tilde:
mov word [edi], '~1'
popad
clc
ret
.tilde:
push edi
add edi, 7
xor ecx, ecx
@@:
; after tilde may be only digits and trailing spaces
cmp byte [edi], '~'
jz .break
cmp byte [edi], ' '
jz .space
cmp byte [edi], '9'
jnz .found
dec edi
jmp @b
.space:
dec edi
inc ecx
jmp @b
.found:
inc byte [edi]
add dword [esp], 8
jmp .zerorest
.break:
jecxz .noplace
inc edi
mov al, '1'
@@:
xchg al, [edi]
inc edi
cmp al, ' '
mov al, '0'
jnz @b
.succ:
pop edi
popad
clc
ret
.noplace:
dec edi
cmp edi, [esp]
jz .err
add dword [esp], 8
mov word [edi], '~1'
inc edi
inc edi
@@:
mov byte [edi], '0'
.zerorest:
inc edi
cmp edi, [esp]
jb @b
pop edi
popad
;clc ; automatically
ret
.err:
pop edi
popad
stc
ret
 
fat_gen_short_name:
; in: esi->long name
; edi->buffer (8+3=11 chars)
; out: buffer filled
pushad
mov eax, ' '
push edi
stosd
stosd
stosd
pop edi
xor eax, eax
movi ebx, 8
lea ecx, [edi+8]
.loop:
lodsb
test al, al
jz .done
call char_toupper
cmp al, ' '
jz .space
cmp al, 80h
ja .big
test [fat_legal_chars+eax], 2
jnz .symbol
.inv_symbol:
mov al, '_'
or bh, 1
.symbol:
cmp al, '.'
jz .dot
.normal_symbol:
dec bl
jns .store
mov bl, 0
.space:
or bh, 1
jmp .loop
.store:
stosb
jmp .loop
.big:
cmp al, 0xB0
jb .normal_symbol
cmp al, 0xE0
jb .inv_symbol
cmp al, 0xF0
jb .normal_symbol
jmp .inv_symbol
.dot:
test bh, 2
jz .firstdot
pop ebx
add ebx, edi
sub ebx, ecx
push ebx
cmp ebx, ecx
jb @f
pop ebx
push ecx
@@:
cmp edi, ecx
jbe .skip
@@:
dec edi
mov al, [edi]
dec ebx
mov [ebx], al
mov byte [edi], ' '
cmp edi, ecx
ja @b
.skip:
mov bh, 3
jmp @f
.firstdot:
cmp bl, 8
jz .space
push edi
or bh, 2
@@:
mov edi, ecx
mov bl, 3
jmp .loop
.done:
test bh, 2
jz @f
pop edi
@@:
lea edi, [ecx-8]
test bh, 1
jz @f
call fat_next_short_name
@@:
popad
ret
 
fat12_free_space:
;---------------------------------------------
;
; returns free space in edi
; rewr.by Mihasik
;---------------------------------------------
 
push eax ebx ecx
 
mov edi, [ebp+FAT.fat12_unpacked_ptr];start of FAT
xor ax, ax;Free cluster=0x0000 in FAT
xor ebx, ebx;counter
mov ecx, [ebp+FAT.LAST_CLUSTER]
inc ecx
cld
rdfs1:
repne scasw
jnz rdfs2 ;if last cluster not 0
inc ebx
test ecx, ecx
jnz rdfs1
rdfs2:
shl ebx, 9;free clusters*512
mov edi, ebx
 
pop ecx ebx eax
ret
 
 
 
set_FAT:
;--------------------------------
; input : EAX = cluster
; EDX = value to save
; EBP = pointer to FAT structure
; output : EDX = old value
;--------------------------------
; out: CF set <=> error
push eax ebx esi
 
cmp eax, 2
jb sfc_error
cmp eax, [ebp+FAT.LAST_CLUSTER]
ja sfc_error
cmp [ebp+FAT.fs_type], 12
je set_FAT12
cmp [ebp+FAT.fs_type], 16
je sfc_1
add eax, eax
sfc_1:
add eax, eax
mov esi, 511
and esi, eax ; esi = position in fat sector
shr eax, 9 ; eax = fat sector
add eax, [ebp+FAT.FAT_START]
mov ebx, [ebp+FAT.fat_cache_ptr]
 
cmp eax, [ebp+FAT.fat_in_cache]; is fat sector already in memory?
je sfc_in_cache ; yes
 
cmp [ebp+FAT.fat_change], 0; is fat changed?
je sfc_no_change ; no
call write_fat_sector; yes. write it into disk
jc sfc_error
 
sfc_no_change:
mov [ebp+FAT.fat_in_cache], eax; save fat sector
call fs_read32_sys
test eax, eax
jne sfc_error
 
 
sfc_in_cache:
cmp [ebp+FAT.fs_type], 16
jne sfc_test32
 
sfc_set16:
xchg [ebx+esi], dx ; save new value and get old value
jmp sfc_write
 
sfc_test32:
mov eax, [ebp+FAT.fatMASK]
 
sfc_set32:
and edx, eax
xor eax, -1 ; mask for high bits
and eax, [ebx+esi] ; get high 4 bits
or eax, edx
mov edx, [ebx+esi] ; get old value
mov [ebx+esi], eax ; save new value
 
sfc_write:
mov [ebp+FAT.fat_change], 1; fat has changed
 
sfc_nonzero:
and edx, [ebp+FAT.fatMASK]
 
sfc_return:
pop esi ebx eax
ret
sfc_error:
stc
jmp sfc_return
 
set_FAT12:
test edx, 0xF000
jnz sfc_error
mov ebx, [ebp+FAT.fat12_unpacked_ptr]
xchg [ebx+eax*2], dx
mov [ebp+FAT.fat_change], 1
pop esi ebx eax
clc
ret
 
get_FAT:
;--------------------------------
; input : EAX = cluster
; EBP = pointer to FAT structure
; output : EAX = next cluster
;--------------------------------
; out: CF set <=> error
push ebx esi
 
cmp [ebp+FAT.fs_type], 12
je get_FAT12
 
cmp [ebp+FAT.fs_type], 16
je gfc_1
add eax, eax
gfc_1:
add eax, eax
mov esi, 511
and esi, eax ; esi = position in fat sector
shr eax, 9 ; eax = fat sector
add eax, [ebp+FAT.FAT_START]
mov ebx, [ebp+FAT.fat_cache_ptr]
 
cmp eax, [ebp+FAT.fat_in_cache]; is fat sector already in memory?
je gfc_in_cache
 
cmp [ebp+FAT.fat_change], 0; is fat changed?
je gfc_no_change ; no
call write_fat_sector; yes. write it into disk
jc hd_error_01
 
gfc_no_change:
mov [ebp+FAT.fat_in_cache], eax
call fs_read32_sys
test eax, eax
jne hd_error_01
 
gfc_in_cache:
mov eax, [ebx+esi]
and eax, [ebp+FAT.fatMASK]
gfc_return:
pop esi ebx
ret
hd_error_01:
stc
jmp gfc_return
 
get_FAT12:
mov ebx, [ebp+FAT.fat12_unpacked_ptr]
movzx eax, word [ebx+eax*2]
pop esi ebx
clc
ret
 
 
get_free_FAT:
;-----------------------------------------------------------
; output : if CARRY=0 EAX = # first cluster found free
; if CARRY=1 disk full
; Note : for more speed need to use fat_cache directly
;-----------------------------------------------------------
push ecx
mov ecx, [ebp+FAT.LAST_CLUSTER]; counter for full disk
mov eax, [ebp+FAT.fatStartScan]
cmp [ebp+FAT.fs_type], 12
jz get_free_FAT12
dec ecx
cmp eax, 2
jb gff_reset
 
gff_test:
cmp eax, [ebp+FAT.LAST_CLUSTER]; if above last cluster start at cluster 2
jbe gff_in_range
gff_reset:
mov eax, 2
 
gff_in_range:
push eax
call get_FAT ; get cluster state
jc gff_not_found_1
 
test eax, eax ; is it free?
pop eax
je gff_found ; yes
inc eax ; next cluster
dec ecx ; is all checked?
jnz gff_test ; no
 
gff_not_found:
pop ecx ; yes. disk is full
stc
ret
 
gff_not_found_1:
pop eax
jmp gff_not_found
 
gff_found:
lea ecx, [eax+1]
mov [ebp+FAT.fatStartScan], ecx
pop ecx
clc
ret
 
get_free_FAT12:
push edx edi
mov edi, [ebp+FAT.fat12_unpacked_ptr]
cmp eax, 2
jb .reset
cmp eax, ecx
jbe @f
.reset:
mov eax, 2
@@:
mov edx, eax
lea edi, [edi+eax*2]
sub ecx, eax
inc ecx
xor eax, eax
repnz scasw
jz .found
cmp edx, 2
jz .notfound
mov edi, [ebp+FAT.fat12_unpacked_ptr]
lea ecx, [edx-2]
repnz scasw
jnz .notfound
.found:
sub edi, [ebp+FAT.fat12_unpacked_ptr]
shr edi, 1
mov [ebp+FAT.fatStartScan], edi
lea eax, [edi-1]
pop edi edx ecx
ret
.notfound:
pop edi edx ecx
stc
ret
 
 
write_fat_sector:
;-----------------------------------------------------------
; write changed fat to disk
;-----------------------------------------------------------
push eax ebx ecx
 
mov [ebp+FAT.fat_change], 0
mov eax, [ebp+FAT.fat_in_cache]
cmp eax, -1
jz write_fat_not_used
mov ebx, [ebp+FAT.fat_cache_ptr]
mov ecx, [ebp+FAT.NUMBER_OF_FATS]
 
write_next_fat:
push eax
call fs_write32_sys
test eax, eax
pop eax
jnz write_fat_not_used
 
add eax, [ebp+FAT.SECTORS_PER_FAT]
dec ecx
jnz write_next_fat
 
write_fat_not_used:
pop ecx ebx eax
ret
 
 
 
 
 
bcd2bin:
;----------------------------------
; input : AL=BCD number (eg. 0x11)
; output : AH=0
; AL=decimal number (eg. 11)
;----------------------------------
xor ah, ah
shl ax, 4
shr al, 4
aad
ret
 
 
get_date_for_file:
;-----------------------------------------------------
; Get date from CMOS and pack day,month,year in AX
; DATE bits 0..4 : day of month 0..31
; 5..8 : month of year 1..12
; 9..15 : count of years from 1980
;-----------------------------------------------------
mov al, 0x7 ;day
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 5
 
mov al, 0x8 ;month
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 4
 
mov al, 0x9 ;year
out 0x70, al
in al, 0x71
call bcd2bin
add ax, 20 ;because CMOS return only the two last
;digit (eg. 2000 -> 00 , 2001 -> 01) and we
rol eax, 9 ;need the difference with 1980 (eg. 2001-1980)
ret
 
 
get_time_for_file:
;-----------------------------------------------------
; Get time from CMOS and pack hour,minute,second in AX
; TIME bits 0..4 : second (the low bit is lost)
; 5..10 : minute 0..59
; 11..15 : hour 0..23
;-----------------------------------------------------
mov al, 0x0 ;second
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 6
 
mov al, 0x2 ;minute
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 6
 
mov al, 0x4 ;hour
out 0x70, al
in al, 0x71
call bcd2bin
rol eax, 11
ret
 
 
set_current_time_for_entry:
;-----------------------------------------------------
; Set current time/date for file entry
; input : ebx = file entry pointer
;-----------------------------------------------------
push eax
call get_time_for_file; update files date/time
mov [ebx+22], ax
call get_date_for_file
mov [ebx+24], ax
pop eax
ret
 
 
 
add_disk_free_space:
;-----------------------------------------------------
; input : ecx = cluster count
; Note : negative = remove clusters from free space
; positive = add clusters to free space
;-----------------------------------------------------
test ecx, ecx ; no change
je add_dfs_no
cmp [ebp+FAT.fs_type], 32 ; free disk space only used by fat32
jne add_dfs_no
 
push eax ebx
mov eax, [ebp+FAT.ADR_FSINFO]
lea ebx, [ebp+FAT.fsinfo_buffer]
call fs_read32_sys
test eax, eax
jnz add_not_fs
 
cmp dword [ebx+0x1fc], 0xaa550000; check sector id
jne add_not_fs
 
add [ebx+0x1e8], ecx
push [ebp+FAT.fatStartScan]
pop dword [ebx+0x1ec]
mov eax, [ebp+FAT.ADR_FSINFO]
call fs_write32_sys
; jc add_not_fs
 
add_not_fs:
pop ebx eax
 
add_dfs_no:
ret
 
 
 
clear_cluster_chain:
;-----------------------------------------------------
; input : eax = first cluster
;-----------------------------------------------------
push eax ecx edx
xor ecx, ecx ; cluster count
 
clean_new_chain:
cmp eax, [ebp+FAT.LAST_CLUSTER]; end of file
ja delete_OK
cmp eax, 2 ; unfinished fat chain or zero length file
jb delete_OK
cmp eax, [ebp+FAT.ROOT_CLUSTER]; don't remove root cluster
jz delete_OK
 
xor edx, edx
call set_FAT ; clear fat entry
jc access_denied_01
 
inc ecx ; update cluster count
mov eax, edx ; old cluster
jmp clean_new_chain
 
delete_OK:
call add_disk_free_space; add clusters to free disk space
clc
access_denied_01:
pop edx ecx eax
ret
 
 
if 0
get_hd_info:
;-----------------------------------------------------------
; output : eax = 0 - ok
; 3 - unknown FS
; 10 - access denied
; edx = cluster size in bytes
; ebx = total clusters on disk
; ecx = free clusters on disk
;-----------------------------------------------------------
cmp [ebp+FAT.fs_type], 16
jz info_fat_ok
cmp [ebp+FAT.fs_type], 32
jz info_fat_ok
xor edx, edx
xor ebx, ebx
xor ecx, ecx
mov eax, ERROR_UNKNOWN_FS
ret
 
info_fat_ok:
; call reserve_hd1
 
xor ecx, ecx ; count of free clusters
mov eax, 2
mov ebx, [ebp+FAT.LAST_CLUSTER]
 
info_cluster:
push eax
call get_FAT ; get cluster info
jc info_access_denied
 
test eax, eax ; is it free?
jnz info_used ; no
inc ecx
 
info_used:
pop eax
inc eax
cmp eax, ebx ; is above last cluster?
jbe info_cluster ; no. test next cluster
 
dec ebx ; cluster count
imul edx, [ebp+FAT.SECTORS_PER_CLUSTER], 512; cluster size in bytes
xor eax, eax
ret
 
info_access_denied:
add esp, 4
xor edx, edx
xor ebx, ebx
xor ecx, ecx
mov eax, ERROR_ACCESS_DENIED
ret
end if
 
update_disk:
cmp [ebp+FAT.fat_change], 0 ; is fat changed?
je upd_no_change
cmp [ebp+FAT.fs_type], 12
jz .fat12
;-----------------------------------------------------------
; write changed fat and cache to disk
;-----------------------------------------------------------
 
call write_fat_sector
jc update_disk_acces_denied
jmp upd_no_change
.fat12:
call restorefatchain
mov [ebp+FAT.fat_change], 0
 
upd_no_change:
 
push esi
mov esi, [ebp+PARTITION.Disk]
call disk_sync
pop esi
update_disk_acces_denied:
ret
 
fat_lock:
lea ecx, [ebp+FAT.Lock]
jmp mutex_lock
fat_unlock:
lea ecx, [ebp+FAT.Lock]
jmp mutex_unlock
 
; \begin{diamond}
uni2ansi_str:
; convert UNICODE zero-terminated string to ASCII-string (codepage 866)
; in: esi->source, edi->buffer (may be esi=edi)
; destroys: eax,esi,edi
lodsw
test ax, ax
jz .done
cmp ax, 0x80
jb .ascii
cmp ax, 0x401
jz .yo1
cmp ax, 0x451
jz .yo2
cmp ax, 0x410
jb .unk
cmp ax, 0x440
jb .rus1
cmp ax, 0x450
jb .rus2
.unk:
mov al, '_'
jmp .doit
.yo1:
mov al, 0xF0 ; 'Ё'
jmp .doit
.yo2:
mov al, 0xF1 ; 'ё'
jmp .doit
.rus1:
; 0x410-0x43F -> 0x80-0xAF
add al, 0x70
jmp .doit
.rus2:
; 0x440-0x44F -> 0xE0-0xEF
add al, 0xA0
.ascii:
.doit:
stosb
jmp uni2ansi_str
.done:
mov byte [edi], 0
ret
 
ansi2uni_char:
; convert ANSI character in al to UNICODE character in ax, using cp866 encoding
mov ah, 0
; 0x00-0x7F - trivial map
cmp al, 0x80
jb .ret
; 0x80-0xAF -> 0x410-0x43F
cmp al, 0xB0
jae @f
add ax, 0x410-0x80
.ret:
ret
@@:
; 0xE0-0xEF -> 0x440-0x44F
cmp al, 0xE0
jb .unk
cmp al, 0xF0
jae @f
add ax, 0x440-0xE0
ret
; 0xF0 -> 0x401
; 0xF1 -> 0x451
@@:
cmp al, 0xF0 ; 'Ё'
jz .yo1
cmp al, 0xF1 ; 'ё'
jz .yo2
.unk:
mov al, '_' ; ah=0
ret
.yo1:
mov ax, 0x401
ret
.yo2:
mov ax, 0x451
ret
 
char_toupper:
; convert character to uppercase, using cp866 encoding
; in: al=symbol
; out: al=converted symbol
cmp al, 'a'
jb .ret
cmp al, 'z'
jbe .az
cmp al, 0xF1 ; 'ё'
jz .yo1
cmp al, 0xA0 ; 'а'
jb .ret
cmp al, 0xE0 ; 'р'
jb .rus1
cmp al, 0xEF ; 'я'
ja .ret
; 0xE0-0xEF -> 0x90-0x9F
sub al, 0xE0-0x90
.ret:
ret
.rus1:
; 0xA0-0xAF -> 0x80-0x8F
.az:
and al, not 0x20
ret
.yo1:
; 0xF1 -> 0xF0
dec ax
ret
 
fat_get_name:
; in: edi->FAT entry
; out: CF=1 - no valid entry
; else CF=0 and ebp->ASCIIZ-name
; (maximum length of filename is 255 (wide) symbols without trailing 0,
; but implementation requires buffer 261 words)
; destroys eax
cmp byte [edi], 0
jz .no
cmp byte [edi], 0xE5
jnz @f
.no:
stc
ret
@@:
cmp byte [edi+11], 0xF
jz .longname
test byte [edi+11], 8
jnz .no
push ecx
push edi ebp
test byte [ebp-4], 1
jnz .unicode_short
 
mov eax, [edi]
mov ecx, [edi+4]
mov [ebp], eax
mov [ebp+4], ecx
 
mov ecx, 8
@@:
cmp byte [ebp+ecx-1], ' '
loope @b
 
mov eax, [edi+8]
cmp al, ' '
je .done
shl eax, 8
mov al, '.'
 
lea ebp, [ebp+ecx+1]
mov [ebp], eax
mov ecx, 3
@@:
rol eax, 8
cmp al, ' '
jne .done
loop @b
dec ebp
.done:
and byte [ebp+ecx+1], 0 ; CF=0
pop ebp edi ecx
ret
.unicode_short:
mov ecx, 8
push ecx
@@:
mov al, [edi]
inc edi
call ansi2uni_char
mov [ebp], ax
inc ebp
inc ebp
loop @b
pop ecx
@@:
cmp word [ebp-2], ' '
jnz @f
dec ebp
dec ebp
loop @b
@@:
mov word [ebp], '.'
inc ebp
inc ebp
mov ecx, 3
push ecx
@@:
mov al, [edi]
inc edi
call ansi2uni_char
mov [ebp], ax
inc ebp
inc ebp
loop @b
pop ecx
@@:
cmp word [ebp-2], ' '
jnz @f
dec ebp
dec ebp
loop @b
dec ebp
dec ebp
@@:
and word [ebp], 0 ; CF=0
pop ebp edi ecx
ret
.longname:
; LFN
mov al, byte [edi]
and eax, 0x3F
dec eax
cmp al, 20
jae .no ; ignore invalid entries
mov word [ebp+260*2], 0 ; force null-terminating for orphans
imul eax, 13*2
add ebp, eax
test byte [edi], 0x40
jz @f
mov word [ebp+13*2], 0
@@:
push eax
; now copy name from edi to ebp ...
mov eax, [edi+1]
mov [ebp], eax ; symbols 1,2
mov eax, [edi+5]
mov [ebp+4], eax ; 3,4
mov eax, [edi+9]
mov [ebp+8], ax ; 5
mov eax, [edi+14]
mov [ebp+10], eax ; 6,7
mov eax, [edi+18]
mov [ebp+14], eax ; 8,9
mov eax, [edi+22]
mov [ebp+18], eax ; 10,11
mov eax, [edi+28]
mov [ebp+22], eax ; 12,13
; ... done
pop eax
sub ebp, eax
test eax, eax
jz @f
; if this is not first entry, more processing required
stc
ret
@@:
; if this is first entry:
test byte [ebp-4], 1
jnz .ret
; buffer at ebp contains UNICODE name, convert it to ANSI
push esi edi
mov esi, ebp
mov edi, ebp
call uni2ansi_str
pop edi esi
.ret:
clc
ret
 
fat_compare_name:
; compares ASCIIZ-names, case-insensitive (cp866 encoding)
; in: esi->name, ebp->name
; out: if names match: ZF=1 and esi->next component of name
; else: ZF=0, esi is not changed
; destroys eax
push ebp esi
.loop:
mov al, [ebp]
inc ebp
call char_toupper
push eax
lodsb
call char_toupper
cmp al, [esp]
jnz .done
pop eax
test al, al
jnz .loop
dec esi
pop eax
pop ebp
xor eax, eax ; set ZF flag
ret
.done:
cmp al, '/'
jnz @f
cmp byte [esp], 0
jnz @f
mov [esp+4], esi
@@:
pop eax
pop esi ebp
ret
 
fat_find_lfn:
; in: esi->name
; [esp+4] = next
; [esp+8] = first
; [esp+C]... - possibly parameters for first and next
; out: CF=1 - file not found, eax=error code
; else CF=0, esi->next name component, edi->direntry
pusha
lea eax, [esp+0Ch+20h]
call dword [eax-4]
jc .reterr
sub esp, 262*2 ; reserve place for LFN
push 0 ; for fat_get_name: read ASCII name
.l1:
lea ebp, [esp+4]
call fat_get_name
jc .l2
call fat_compare_name
jz .found
.l2:
mov ebp, [esp+8+262*2+4]
lea eax, [esp+0Ch+20h+262*2+4]
call dword [eax-8]
jnc .l1
add esp, 262*2+4
.reterr:
mov [esp+28], eax
stc
popa
ret
.found:
add esp, 262*2+4
mov ebp, [esp+8]
; if this is LFN entry, advance to true entry
cmp byte [edi+11], 0xF
jnz @f
lea eax, [esp+0Ch+20h]
call dword [eax-8]
jc .reterr
@@:
add esp, 8 ; CF=0
push esi
push edi
popa
ret
 
fat_time_to_bdfe:
; in: eax=FAT time
; out: eax=BDFE time
push ecx edx
mov ecx, eax
mov edx, eax
shr eax, 11
shl eax, 16 ; hours
and edx, 0x1F
add edx, edx
mov al, dl ; seconds
shr ecx, 5
and ecx, 0x3F
mov ah, cl ; minutes
pop edx ecx
ret
 
fat_date_to_bdfe:
push ecx edx
mov ecx, eax
mov edx, eax
shr eax, 9
add ax, 1980
shl eax, 16 ; year
and edx, 0x1F
mov al, dl ; day
shr ecx, 5
and ecx, 0xF
mov ah, cl ; month
pop edx ecx
ret
 
bdfe_to_fat_time:
push edx
mov edx, eax
shr eax, 16
and dh, 0x3F
shl eax, 6
or al, dh
shr dl, 1
and dl, 0x1F
shl eax, 5
or al, dl
pop edx
ret
 
bdfe_to_fat_date:
push edx
mov edx, eax
shr eax, 16
sub ax, 1980
and dh, 0xF
shl eax, 4
or al, dh
and dl, 0x1F
shl eax, 5
or al, dl
pop edx
ret
 
fat_entry_to_bdfe:
; convert FAT entry at edi to BDFE (block of data of folder entry) at esi, advance esi
; destroys eax
mov eax, [ebp-4]
mov [esi+4], eax ; ASCII/UNICODE name
fat_entry_to_bdfe2:
movzx eax, byte [edi+11]
mov [esi], eax ; attributes
movzx eax, word [edi+14]
call fat_time_to_bdfe
mov [esi+8], eax ; creation time
movzx eax, word [edi+16]
call fat_date_to_bdfe
mov [esi+12], eax ; creation date
and dword [esi+16], 0 ; last access time is not supported on FAT
movzx eax, word [edi+18]
call fat_date_to_bdfe
mov [esi+20], eax ; last access date
movzx eax, word [edi+22]
call fat_time_to_bdfe
mov [esi+24], eax ; last write time
movzx eax, word [edi+24]
call fat_date_to_bdfe
mov [esi+28], eax ; last write date
mov eax, [edi+28]
mov [esi+32], eax ; file size (low dword)
xor eax, eax
mov [esi+36], eax ; file size (high dword)
test ebp, ebp
jz .ret
push ecx edi
lea edi, [esi+40]
mov esi, ebp
test byte [esi-4], 1
jz .ansi
mov ecx, 260/2
rep movsd
mov [edi-2], ax
@@:
mov esi, edi
pop edi ecx
.ret:
ret
.ansi:
mov ecx, 264/4
rep movsd
mov [edi-1], al
jmp @b
 
bdfe_to_fat_entry:
; convert BDFE at edx to FAT entry at edi
; destroys eax
; attributes byte
test byte [edi+11], 8 ; volume label?
jnz @f
mov al, [edx]
and al, 0x27
and byte [edi+11], 0x10
or byte [edi+11], al
@@:
mov eax, [edx+8]
call bdfe_to_fat_time
mov [edi+14], ax ; creation time
mov eax, [edx+12]
call bdfe_to_fat_date
mov [edi+16], ax ; creation date
mov eax, [edx+20]
call bdfe_to_fat_date
mov [edi+18], ax ; last access date
mov eax, [edx+24]
call bdfe_to_fat_time
mov [edi+22], ax ; last write time
mov eax, [edx+28]
call bdfe_to_fat_date
mov [edi+24], ax ; last write date
ret
 
hd_find_lfn:
; in: ebp -> FAT structure
; in: esi+[esp+4] -> name
; out: CF=1 - file not found, eax=error code
; else CF=0 and edi->direntry, eax=sector
; destroys eax
push esi edi
push 0
push 0
push fat1x_root_first
push fat1x_root_next
mov eax, [ebp+FAT.ROOT_CLUSTER]
cmp [ebp+FAT.fs_type], 32
jz .fat32
.loop:
and [ebp+FAT.longname_sec1], 0
and [ebp+FAT.longname_sec2], 0
call fat_find_lfn
jc .notfound
cmp byte [esi], 0
jz .found
.continue:
test byte [edi+11], 10h
jz .notfound
and dword [esp+12], 0
mov eax, [edi+20-2]
mov ax, [edi+26] ; cluster
.fat32:
mov [esp+8], eax
mov dword [esp+4], fat_notroot_first
mov dword [esp], fat_notroot_next
jmp .loop
.notfound:
add esp, 16
pop edi esi
stc
ret 4
.found:
lea eax, [esp+4+24]
cmp dword [eax], 0
jz @f
mov esi, [eax]
and dword [eax], 0
jmp .continue
@@:
lea eax, [esp+8]
cmp dword [eax], 0
jz .root
call fat_get_sector
jmp .cmn
.root:
mov eax, [eax+4]
add eax, [ebp+FAT.ROOT_START]
.cmn:
add esp, 20 ; CF=0
pop esi
ret 4
 
;----------------------------------------------------------------
; fat_Read - FAT implementation of reading a file
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_Read:
call fat_lock
push edi
cmp byte [esi], 0
jnz @f
.noaccess:
pop edi
.noaccess_2:
call fat_unlock
or ebx, -1
mov eax, ERROR_ACCESS_DENIED
ret
 
@@:
stdcall hd_find_lfn, [esp+4+4]
jnc .found
pop edi
push eax
call fat_unlock
pop eax
or ebx, -1
ret
 
.found:
test byte [edi+11], 0x10; do not allow read directories
jnz .noaccess
cmp dword [ebx+8], 0
jz @f
xor ebx, ebx
.reteof:
call fat_unlock
mov eax, ERROR_END_OF_FILE
pop edi
ret
@@:
mov ecx, [ebx+12] ; size
mov edx, [ebx+16] ; pointer
mov ebx, [ebx+4] ; file offset
push edx
push 0
mov eax, [edi+28]
sub eax, ebx
jb .eof
cmp eax, ecx
jae @f
mov ecx, eax
mov byte [esp], 6
@@:
mov eax, [edi+20-2]
mov ax, [edi+26]
; now eax=cluster, ebx=position, ecx=count, edx=buffer for data
.new_cluster:
jecxz .new_sector
cmp eax, 2
jb .eof
cmp eax, [ebp+FAT.fatRESERVED]
jae .eof
mov [ebp+FAT.cluster_tmp], eax
dec eax
dec eax
mov edi, [ebp+FAT.SECTORS_PER_CLUSTER]
imul eax, edi
add eax, [ebp+FAT.DATA_START]
.new_sector:
test ecx, ecx
jz .done
sub ebx, 512
jae .skip
add ebx, 512
jnz .force_buf
cmp ecx, 512
jb .force_buf
; we may read directly to given buffer
push eax ebx
mov ebx, edx
call fs_read32_app
test eax, eax
pop ebx eax
jne .noaccess_1
add edx, 512
sub ecx, 512
jmp .skip
.force_buf:
; we must read sector to temporary buffer and then copy it to destination
push eax ebx
lea ebx, [ebp+FAT.buffer]
call fs_read32_app
test eax, eax
mov eax, ebx
pop ebx
jne .noaccess_3
add eax, ebx
push ecx
add ecx, ebx
cmp ecx, 512
jbe @f
mov ecx, 512
@@:
sub ecx, ebx
mov ebx, edx
call memmove
add edx, ecx
sub [esp], ecx
pop ecx
pop eax
xor ebx, ebx
.skip:
inc eax
dec edi
jnz .new_sector
mov eax, [ebp+FAT.cluster_tmp]
call get_FAT
jc .noaccess_1
 
jmp .new_cluster
.noaccess_3:
pop eax
.noaccess_1:
pop eax
push ERROR_DEVICE
.done:
mov ebx, edx
call fat_unlock
pop eax edx edi
sub ebx, edx
ret
.eof:
mov ebx, edx
pop eax edx
sub ebx, edx
jmp .reteof
 
;----------------------------------------------------------------
; fat_ReadFolder - FAT implementation of reading a folder
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_ReadFolder:
call fat_lock
mov eax, [ebp+FAT.ROOT_CLUSTER]
push edi
cmp byte [esi], 0
jz .doit
stdcall hd_find_lfn, [esp+4+4]
jnc .found
pop edi
push eax
call fat_unlock
pop eax
or ebx, -1
ret
.found:
test byte [edi+11], 0x10 ; do not allow read files
jnz .found_dir
pop edi
call fat_unlock
or ebx, -1
mov eax, ERROR_ACCESS_DENIED
ret
.found_dir:
mov eax, [edi+20-2]
mov ax, [edi+26] ; eax=cluster
.doit:
push esi
sub esp, 262*2 ; reserve space for LFN
push dword [ebx+8] ; for fat_get_name: read ANSI/UNICODE name
mov edx, [ebx+16] ; pointer to buffer
; init header
push eax
mov edi, edx
mov ecx, 32/4
xor eax, eax
rep stosd
pop eax
mov byte [edx], 1 ; version
mov esi, edi ; esi points to BDFE
mov ecx, [ebx+12] ; number of blocks to read
mov ebx, [ebx+4] ; index of the first block
.new_cluster:
mov [ebp+FAT.cluster_tmp], eax
test eax, eax
jnz @f
cmp [ebp+FAT.fs_type], 32
jz .notfound
mov eax, [ebp+FAT.ROOT_START]
push [ebp+FAT.ROOT_SECTORS]
push ebx
jmp .new_sector
@@:
dec eax
dec eax
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
push [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
push ebx
.new_sector:
lea ebx, [ebp+FAT.buffer]
mov edi, ebx
push eax
call fs_read32_sys
test eax, eax
pop eax
jnz .notfound2
add ebx, 512
push eax
.l1:
push ebp
lea ebp, [esp+20]
call fat_get_name
pop ebp
jc .l2
cmp byte [edi+11], 0xF
jnz .do_bdfe
add edi, 0x20
cmp edi, ebx
jb .do_bdfe
pop eax
inc eax
dec dword [esp+4]
jnz @f
mov eax, [ebp+FAT.cluster_tmp]
test eax, eax
jz .done
call get_FAT
jc .notfound2
cmp eax, 2
jb .done
cmp eax, [ebp+FAT.fatRESERVED]
jae .done
push eax
mov eax, [ebp+FAT.SECTORS_PER_CLUSTER]
mov [esp+8], eax
pop eax
mov [ebp+FAT.cluster_tmp], eax
dec eax
dec eax
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
@@:
lea ebx, [ebp+FAT.buffer]
mov edi, ebx
push eax
call fs_read32_sys
test eax, eax
pop eax
jnz .notfound2
add ebx, 512
push eax
.do_bdfe:
inc dword [edx+8] ; new file found
dec dword [esp+4]
jns .l2
dec ecx
js .l2
inc dword [edx+4] ; new file block copied
push ebp
lea ebp, [esp+20]
call fat_entry_to_bdfe
pop ebp
.l2:
add edi, 0x20
cmp edi, ebx
jb .l1
pop eax
inc eax
dec dword [esp+4]
jnz .new_sector
mov eax, [ebp+FAT.cluster_tmp]
test eax, eax
jz .done
call get_FAT
jc .notfound2
cmp eax, 2
jb .done
cmp eax, [ebp+FAT.fatRESERVED]
jae .done
push eax
mov eax, [ebp+FAT.SECTORS_PER_CLUSTER]
mov [esp+8], eax
pop eax
pop ebx
add esp, 4
jmp .new_cluster
.notfound2:
add esp, 8
.notfound:
add esp, 262*2+4
pop esi edi
mov ebx, [edx+4]
call fat_unlock
mov eax, ERROR_DEVICE
ret
.done:
add esp, 262*2+4+8
mov ebx, [edx+4]
xor eax, eax
dec ecx
js @f
mov al, ERROR_END_OF_FILE
@@:
push eax
call fat_unlock
pop eax
pop esi edi
ret
 
fat1x_root_next:
push ecx
lea ecx, [ebp+FAT.buffer+0x200-0x20]
cmp edi, ecx
jae fat1x_root_next_sector
pop ecx
add edi, 0x20
ret ; CF=0
fat1x_root_next_sector:
; read next sector
push [ebp+FAT.longname_sec2]
pop [ebp+FAT.longname_sec1]
mov ecx, [eax+4]
push ecx
add ecx, [ebp+FAT.ROOT_START]
mov [ebp+FAT.longname_sec2], ecx
pop ecx
inc ecx
mov [eax+4], ecx
cmp ecx, [ebp+FAT.ROOT_SECTORS]
pop ecx
jb fat1x_root_first
mov eax, ERROR_FILE_NOT_FOUND
stc
ret
fat1x_root_first:
mov eax, [eax+4]
add eax, [ebp+FAT.ROOT_START]
push ebx
lea edi, [ebp+FAT.buffer]
mov ebx, edi
call fs_read32_sys
pop ebx
test eax, eax
jnz .readerr
ret ; CF=0
.readerr:
mov eax, ERROR_DEVICE
stc
ret
.notfound:
mov eax, ERROR_FILE_NOT_FOUND
stc
ret
fat1x_root_begin_write:
push edi eax
call fat1x_root_first
pop eax edi
ret
fat1x_root_end_write:
pusha
mov eax, [eax+4]
add eax, [ebp+FAT.ROOT_START]
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
popa
ret
fat1x_root_next_write:
push ecx
lea ecx, [ebp+FAT.buffer+0x200]
cmp edi, ecx
jae @f
pop ecx
ret
@@:
call fat1x_root_end_write
jmp fat1x_root_next_sector
fat1x_root_extend_dir:
stc
ret
 
fat_notroot_next:
push ecx
lea ecx, [ebp+FAT.buffer+0x200-0x20]
cmp edi, ecx
jae fat_notroot_next_sector
pop ecx
add edi, 0x20
ret ; CF=0
fat_notroot_next_sector:
push [ebp+FAT.longname_sec2]
pop [ebp+FAT.longname_sec1]
push eax
call fat_get_sector
mov [ebp+FAT.longname_sec2], eax
pop eax
mov ecx, [eax+4]
inc ecx
cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
jae fat_notroot_next_cluster
mov [eax+4], ecx
jmp @f
fat_notroot_next_cluster:
push eax
mov eax, [eax]
call get_FAT
mov ecx, eax
pop eax
jc fat_notroot_first.deverr
cmp ecx, 2
jb fat_notroot_next_err
cmp ecx, [ebp+FAT.fatRESERVED]
jae fat_notroot_next_err
mov [eax], ecx
and dword [eax+4], 0
@@:
pop ecx
fat_notroot_first:
call fat_get_sector
push ebx
lea edi, [ebp+FAT.buffer]
mov ebx, edi
call fs_read32_sys
pop ebx
test eax, eax
jz .ret ; CF=0
push ecx
.deverr:
pop ecx
mov eax, ERROR_DEVICE
stc
.ret:
ret
fat_notroot_next_err:
pop ecx
mov eax, ERROR_FILE_NOT_FOUND
stc
ret
fat_notroot_begin_write:
push eax edi
call fat_notroot_first
pop edi eax
ret
fat_notroot_end_write:
call fat_get_sector
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
pop ebx
ret
fat_notroot_next_write:
push ecx
lea ecx, [ebp+FAT.buffer+0x200]
cmp edi, ecx
jae @f
pop ecx
ret
@@:
push eax
call fat_notroot_end_write
pop eax
jmp fat_notroot_next_sector
fat_notroot_extend_dir:
push eax
call get_free_FAT
jnc .found
pop eax
ret ; CF=1
.found:
push edx
mov edx, [ebp+FAT.fatEND]
call set_FAT
jc .writeerr
mov edx, eax
mov eax, [esp+4]
mov eax, [eax]
push edx
call set_FAT
pop edx
jnc @f
.writeerr:
pop edx
pop eax
stc
ret
@@:
push ecx
or ecx, -1
call add_disk_free_space
; zero new cluster
mov ecx, 512/4
lea edi, [ebp+FAT.buffer]
push edi
xor eax, eax
rep stosd
pop edi
pop ecx
mov eax, [esp+4]
mov [eax], edx
and dword [eax+4], 0
pop edx
mov eax, [eax]
dec eax
dec eax
push ebx ecx
mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
imul eax, ecx
add eax, [ebp+FAT.DATA_START]
mov ebx, edi
@@:
push eax
call fs_write32_sys
pop eax
inc eax
loop @b
pop ecx ebx eax
clc
ret
 
fat_get_sector:
push ecx
mov ecx, [eax]
dec ecx
dec ecx
imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
add ecx, [ebp+FAT.DATA_START]
add ecx, [eax+4]
mov eax, ecx
pop ecx
ret
 
fshrad:
call fat_unlock
mov eax, ERROR_ACCESS_DENIED
xor ebx, ebx
ret
 
;----------------------------------------------------------------
; fat_CreateFolder - FAT implementation of creating a folder
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_CreateFolder:
push 1
jmp fat_Rewrite.common
 
;----------------------------------------------------------------
; fat_Rewrite - FAT implementation of creating a new file
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_Rewrite:
push 0
.common:
call fat_lock
pop eax
cmp byte [esi], 0
jz fshrad
mov ecx, [ebx+12]
mov edx, [ebx+16]
pushad
xor edi, edi
mov edx, [esp+4+20h]
push esi
test edx, edx
jz @f
mov esi, edx
@@:
lodsb
test al, al
jz @f
cmp al, '/'
jnz @b
lea edi, [esi-1]
jmp @b
@@:
pop esi
test edi, edi
jnz .noroot
test edx, edx
jnz .hasebp
mov edx, [ebp+FAT.ROOT_CLUSTER]
cmp [ebp+FAT.fs_type], 32
jz .pushnotroot
xor edx, edx
push edx
push fat1x_root_extend_dir
push fat1x_root_end_write
push fat1x_root_next_write
push fat1x_root_begin_write
push edx
push edx
push fat1x_root_first
push fat1x_root_next
jmp .common1
.hasebp:
mov eax, ERROR_ACCESS_DENIED
cmp byte [edx], 0
jz .ret1
stdcall hd_find_lfn, 0
mov esi, [esp+4+20h]
jc .ret1
jmp .common0
.noroot:
mov eax, ERROR_ACCESS_DENIED
cmp byte [edi+1], 0
jz .ret1
; check existence
mov byte [edi], 0
push edi
stdcall hd_find_lfn, [esp+4+24h]
pop esi
mov byte [esi], '/'
jnc @f
.notfound0:
mov eax, ERROR_FILE_NOT_FOUND
.ret1:
mov [esp+28], eax
call fat_unlock
popad
xor ebx, ebx
ret
@@:
inc esi
.common0:
test byte [edi+11], 0x10 ; must be directory
mov eax, ERROR_ACCESS_DENIED
jz .ret1
mov edx, [edi+20-2]
mov dx, [edi+26] ; ebp=cluster
mov eax, ERROR_FAT_TABLE
cmp edx, 2
jb .ret1
.pushnotroot:
push edx
push fat_notroot_extend_dir
push fat_notroot_end_write
push fat_notroot_next_write
push fat_notroot_begin_write
push 0
push edx
push fat_notroot_first
push fat_notroot_next
.common1:
call fat_find_lfn
jc .notfound
; found
test byte [edi+11], 10h
jz .exists_file
; found directory; if we are creating directory, return OK,
; if we are creating file, say "access denied"
add esp, 36
call fat_unlock
popad
test al, al
mov eax, ERROR_ACCESS_DENIED
jz @f
mov al, 0
@@:
xor ebx, ebx
ret
.exists_file:
; found file; if we are creating directory, return "access denied",
; if we are creating file, delete existing file and continue
cmp byte [esp+36+28], 0
jz @f
add esp, 36
call fat_unlock
popad
mov eax, ERROR_ACCESS_DENIED
xor ebx, ebx
ret
@@:
; delete FAT chain
push edi
xor eax, eax
mov dword [edi+28], eax ; zero size
xor ecx, ecx
mov eax, [edi+20-2]
mov ax, [edi+26]
mov word [edi+20], cx
mov word [edi+26], cx
test eax, eax
jz .done1
@@:
cmp eax, [ebp+FAT.fatRESERVED]
jae .done1
push edx
xor edx, edx
call set_FAT
mov eax, edx
pop edx
jc .done1
inc ecx
jmp @b
.done1:
pop edi
call get_time_for_file
mov [edi+22], ax
call get_date_for_file
mov [edi+24], ax
mov [edi+18], ax
or byte [edi+11], 20h ; set 'archive' attribute
jmp .doit
.notfound:
; file is not found; generate short name
call fat_name_is_legal
jc @f
add esp, 36
call fat_unlock
popad
mov eax, ERROR_FILE_NOT_FOUND
xor ebx, ebx
ret
@@:
sub esp, 12
mov edi, esp
call fat_gen_short_name
.test_short_name_loop:
push esi edi ecx
mov esi, edi
lea eax, [esp+12+12+8]
mov edx, [eax+24]
mov [eax], edx
and dword [eax+4], 0
call dword [eax-4]
jc .found
.test_short_name_entry:
cmp byte [edi+11], 0xF
jz .test_short_name_cont
mov ecx, 11
push esi edi
repz cmpsb
pop edi esi
jz .short_name_found
.test_short_name_cont:
lea eax, [esp+12+12+8]
call dword [eax-8]
jnc .test_short_name_entry
jmp .found
.short_name_found:
pop ecx edi esi
call fat_next_short_name
jnc .test_short_name_loop
.disk_full:
add esp, 12+36
call fat_unlock
popa
mov eax, ERROR_DISK_FULL
xor ebx, ebx
ret
.found:
pop ecx edi esi
; now find space in directory
; we need to save LFN <=> LFN is not equal to short name <=> generated name contains '~'
mov al, '~'
push ecx edi
mov ecx, 8
repnz scasb
movi eax, 1 ; 1 entry
jnz .notilde
; we need ceil(strlen(esi)/13) additional entries = floor((strlen(esi)+12+13)/13) total
xor eax, eax
@@:
cmp byte [esi], 0
jz @f
inc esi
inc eax
jmp @b
@@:
sub esi, eax
add eax, 12+13
mov ecx, 13
push edx
cdq
div ecx
pop edx
.notilde:
push -1
push -1
push -1
; find <eax> successive entries in directory
xor ecx, ecx
push eax
lea eax, [esp+16+8+12+8]
mov edx, [eax+24]
mov [eax], edx
and dword [eax+4], 0
call dword [eax-4]
pop eax
jnc .scan_dir
.fsfrfe3:
add esp, 12+8+12+36
call fat_unlock
popad
mov eax, ERROR_DEVICE
xor ebx, ebx
ret
.scan_dir:
cmp byte [edi], 0
jz .free
cmp byte [edi], 0xE5
jz .free
xor ecx, ecx
.scan_cont:
push eax
lea eax, [esp+16+8+12+8]
call dword [eax-8]
mov edx, eax
pop eax
jnc .scan_dir
cmp edx, ERROR_DEVICE
jz .fsfrfe3
push eax
lea eax, [esp+16+8+12+8]
call dword [eax+20] ; extend directory
pop eax
jnc .scan_dir
add esp, 12+8+12+36
call fat_unlock
popad
mov eax, ERROR_DISK_FULL
xor ebx, ebx
ret
.free:
test ecx, ecx
jnz @f
mov [esp], edi
mov ecx, [esp+12+8+12+8]
mov [esp+4], ecx
mov ecx, [esp+12+8+12+12]
mov [esp+8], ecx
xor ecx, ecx
@@:
inc ecx
cmp ecx, eax
jb .scan_cont
; found!
push esi ecx
; If creating a directory, allocate one data cluster now and fail immediately
; if this is impossible. This prevents from creating an invalid directory entry
; on a full disk.
; yup, the argument is quite non-intuitive... but what should I do if
; the entire function uses such arguments? BTW, it refers to al from pushad,
; which in turn is filled with 0 in fat_Rewrite and 1 in fat_CreateFolder.
cmp byte [esp+8+12+8+12+36+28], 0
jz .no.preallocate.folder.data
call get_free_FAT
jnc @f
add esp, 8+12+8
jmp .disk_full
@@:
mov [esp+8+12+8+12+36+20], eax ; store the cluster somewhere
.no.preallocate.folder.data:
; calculate name checksum
mov esi, [esp+8+12]
mov ecx, 11
xor eax, eax
@@:
ror al, 1
add al, [esi]
inc esi
loop @b
pop ecx esi
pop edi
pop dword [esp+8+12+12]
pop dword [esp+8+12+12]
; edi points to first entry in free chunk
dec ecx
jz .nolfn
push esi
push eax
lea eax, [esp+8+8+12+8]
call dword [eax+8] ; begin write
mov al, 40h
.writelfn:
or al, cl
mov esi, [esp+4]
push ecx
dec ecx
imul ecx, 13
add esi, ecx
stosb
mov cl, 5
call fat_read_symbols
mov ax, 0xF
stosw
mov al, [esp+4]
stosb
mov cl, 6
call fat_read_symbols
xor eax, eax
stosw
mov cl, 2
call fat_read_symbols
pop ecx
lea eax, [esp+8+8+12+8]
call dword [eax+12] ; next write
xor eax, eax
loop .writelfn
pop eax
pop esi
; lea eax, [esp+8+12+8]
; call dword [eax+16] ; end write
.nolfn:
xchg esi, [esp]
mov ecx, 11
rep movsb
mov word [edi], 20h ; attributes
sub edi, 11
pop esi ecx
add esp, 12
mov byte [edi+13], 0 ; tenths of a second at file creation time
call get_time_for_file
mov [edi+14], ax ; creation time
mov [edi+22], ax ; last write time
call get_date_for_file
mov [edi+16], ax ; creation date
mov [edi+24], ax ; last write date
mov [edi+18], ax ; last access date
xor ecx, ecx
mov word [edi+20], cx ; high word of cluster
mov word [edi+26], cx ; low word of cluster - to be filled
mov dword [edi+28], ecx ; file size - to be filled
cmp byte [esp+36+28], cl
jz .doit
; create directory
mov byte [edi+11], 10h ; attributes: folder
mov esi, edi
lea eax, [esp+8]
call dword [eax+16] ; flush directory
mov eax, [esp+36+20] ; extract saved cluster
mov [esp+36+20], edi ; this is needed for calculating arg of add_disk_free_space!
push ecx
mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
shl ecx, 9
push ecx
push edi
jmp .doit2
.doit:
mov esi, [esp+36+20]
lea eax, [esp+8]
call dword [eax+16] ; flush directory
push ecx
mov ecx, [esp+4+36+24]
push ecx
push edi
test ecx, ecx
jz .done
call get_free_FAT
jc .diskfull
.doit2:
push eax
mov [edi+26], ax
shr eax, 16
mov [edi+20], ax
lea eax, [esp+16+8]
call dword [eax+16] ; flush directory
pop eax
push edx
mov edx, [ebp+FAT.fatEND]
call set_FAT
pop edx
.write_cluster:
push eax
dec eax
dec eax
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
push [ebp+FAT.SECTORS_PER_CLUSTER]
; write data
.write_sector:
cmp byte [esp+20+36+28], 0
jnz .writedir
mov ecx, 512
cmp dword [esp+12], ecx
jb .writeshort
; we can write directly from given buffer
mov ebx, esi
add esi, ecx
jmp .writecommon
.writeshort:
mov ecx, [esp+12]
push ecx
lea edi, [ebp+FAT.buffer]
mov ebx, edi
rep movsb
.writedircont:
lea ecx, [ebp+FAT.buffer+0x200]
sub ecx, edi
push eax
xor eax, eax
rep stosb
pop eax
pop ecx
.writecommon:
push eax
call fs_write32_app
test eax, eax
pop eax
jnz .writeerr
inc eax
sub dword [esp+12], ecx
jz .writedone
dec dword [esp]
jnz .write_sector
pop eax
; allocate new cluster
pop eax
mov ecx, eax
call get_free_FAT
jc .diskfull
push edx
mov edx, [ebp+FAT.fatEND]
call set_FAT
xchg eax, ecx
mov edx, ecx
call set_FAT
pop edx
xchg eax, ecx
jmp .write_cluster
.diskfull:
mov eax, ERROR_DISK_FULL
jmp .ret
.writeerr:
pop eax eax
sub esi, ecx
mov eax, ERROR_DEVICE
jmp .ret
.writedone:
pop eax eax
.done:
xor eax, eax
.ret:
pop edi ecx
sub esi, [esp+4+36+20]
mov [esp+4+36+28], eax
mov [esp+4+36+16], esi
lea eax, [esp+12]
call dword [eax+8]
mov [edi+28], esi
call dword [eax+16]
mov [esp+36+16], ebx
lea eax, [esi+511]
shr eax, 9
mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
lea eax, [eax+ecx-1]
xor edx, edx
div ecx
pop ecx
sub ecx, eax
call add_disk_free_space
add esp, 36
call update_disk
call fat_unlock
popad
ret
.writedir:
push 512
lea edi, [ebp+FAT.buffer]
mov ebx, edi
mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
shl ecx, 9
cmp ecx, [esp+16]
jnz .writedircont
dec dword [esp+20]
push esi
mov ecx, 32/4
rep movsd
pop esi
mov dword [edi-32], '. '
mov dword [edi-32+4], ' '
mov dword [edi-32+8], ' '
mov byte [edi-32+11], 10h
push esi
mov ecx, 32/4
rep movsd
pop esi
mov dword [edi-32], '.. '
mov dword [edi-32+4], ' '
mov dword [edi-32+8], ' '
mov byte [edi-32+11], 10h
mov ecx, [esp+20+36]
cmp ecx, [ebp+FAT.ROOT_CLUSTER]
jnz @f
xor ecx, ecx
@@:
mov word [edi-32+26], cx
shr ecx, 16
mov [edi-32+20], cx
jmp .writedircont
 
fat_read_symbol:
or ax, -1
test esi, esi
jz .retFFFF
lodsb
test al, al
jnz ansi2uni_char
xor eax, eax
xor esi, esi
.retFFFF:
ret
 
fat_read_symbols:
call fat_read_symbol
stosw
loop fat_read_symbols
ret
 
 
fat_Write.access_denied:
push ERROR_ACCESS_DENIED
fat_Write.ret0:
pop eax
xor ebx, ebx
ret
 
fat_Write.ret11:
push ERROR_DEVICE
jmp fat_Write.ret0
 
;----------------------------------------------------------------
; fat_Write - FAT implementation of writing to file
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_Write:
cmp byte [esi], 0
jz .access_denied
call fat_lock
push edi
stdcall hd_find_lfn, [esp+4+4]
jnc .found
pop edi
push eax
call fat_unlock
jmp .ret0
.found:
; FAT does not support files larger than 4GB
cmp dword [ebx+8], 0
jz @f
.eof:
pop edi
push ERROR_END_OF_FILE
call fat_unlock
jmp .ret0
@@:
mov ecx, [ebx+12]
mov edx, [ebx+16]
mov ebx, [ebx+4]
; now edi points to direntry, ebx=start byte to write,
; ecx=number of bytes to write, edx=data pointer
 
; extend file if needed
add ecx, ebx
jc .eof ; FAT does not support files larger than 4GB
push edx
push eax ; save directory sector
push 0 ; return value=0
 
call get_time_for_file
mov [edi+22], ax ; last write time
call get_date_for_file
mov [edi+24], ax ; last write date
mov [edi+18], ax ; last access date
 
push dword [edi+28] ; save current file size
cmp ecx, [edi+28]
jbe .length_ok
cmp ecx, ebx
jz .length_ok
call hd_extend_file
jnc .length_ok
mov [esp+4], eax
; hd_extend_file can return three error codes: FAT table error, device error or disk full.
; First two cases are fatal errors, in third case we may write some data
cmp al, ERROR_DISK_FULL
jz .disk_full
call fat_unlock
pop eax
pop eax
pop ecx
pop edx
pop edi
xor ebx, ebx
ret
.disk_full:
; correct number of bytes to write
mov ecx, [edi+28]
cmp ecx, ebx
ja .length_ok
push 0
.ret:
pop eax
sub edx, [esp+12]
mov ebx, edx ; ebx=number of written bytes
call update_disk
test eax, eax
jz @f
mov byte [esp+4], ERROR_DEVICE
@@:
call fat_unlock
pop eax
pop eax
pop ecx
pop edx
pop edi
ret
.length_ok:
mov esi, [edi+28]
mov eax, [edi+20-2]
mov ax, [edi+26]
mov edi, eax ; edi=current cluster
push 0 ; current sector in cluster
; save directory
mov eax, [esp+12]
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
pop ebx
test eax, eax
jz @f
.device_err:
mov byte [esp+8], ERROR_DEVICE
jmp .ret
.fat_err:
mov byte [esp+8], ERROR_FAT_TABLE
jmp .ret
@@:
 
; now ebx=start pos, ecx=end pos, both lie inside file
sub ecx, ebx
jz .ret
.write_loop:
; skip unmodified sectors
cmp dword [esp+4], 0x200
jb .modify
sub ebx, 0x200
jae .skip
add ebx, 0x200
.modify:
; get length of data in current sector
push ecx
sub ebx, 0x200
jb .hasdata
neg ebx
xor ecx, ecx
jmp @f
.hasdata:
neg ebx
cmp ecx, ebx
jbe @f
mov ecx, ebx
@@:
; get current sector number
mov eax, edi
dec eax
dec eax
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
add eax, [esp+4]
; load sector if needed
cmp dword [esp+8], 0 ; we don't need to read uninitialized data
jz .noread
cmp ecx, 0x200 ; we don't need to read sector if it is fully rewritten
jz .noread
cmp ecx, esi ; (same for the last sector)
jz .noread
push eax ebx
lea ebx, [ebp+FAT.buffer]
call fs_read32_app
test eax, eax
pop ebx eax
jz @f
.device_err2:
pop ecx
jmp .device_err
@@:
.noread:
; zero uninitialized data if file was extended (because hd_extend_file does not this)
push eax ecx edi
xor eax, eax
mov ecx, 0x200
sub ecx, [esp+8+12]
jbe @f
lea edi, [ebp+FAT.buffer]
add edi, [esp+8+12]
rep stosb
@@:
; zero uninitialized data in the last sector
mov ecx, 0x200
sub ecx, esi
jbe @f
lea edi, [ebp+FAT.buffer+esi]
rep stosb
@@:
pop edi ecx
; copy new data
mov eax, edx
neg ebx
jecxz @f
lea ebx, [ebp+FAT.buffer+0x200+ebx]
call memmove
xor ebx, ebx
@@:
pop eax
; save sector
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_app
pop ebx
test eax, eax
jnz .device_err2
add edx, ecx
sub [esp], ecx
pop ecx
jz .ret
.skip:
; next sector
pop eax
inc eax
push eax
cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER]
jb @f
and dword [esp], 0
mov eax, edi
call get_FAT
mov edi, eax
jc .device_err
cmp edi, 2
jb .fat_err
cmp edi, [ebp+FAT.fatRESERVED]
jae .fat_err
@@:
sub esi, 0x200
jae @f
xor esi, esi
@@:
sub dword [esp+4], 0x200
jae @f
and dword [esp+4], 0
@@:
jmp .write_loop
 
hd_extend_file.zero_size:
xor eax, eax
jmp hd_extend_file.start_extend
 
; extends file on hd to given size (new data area is undefined)
; in: edi->direntry, ecx=new size
; out: CF=0 => OK, eax=0
; CF=1 => error, eax=code (ERROR_FAT_TABLE or ERROR_DISK_FULL or ERROR_DEVICE)
hd_extend_file:
push esi
mov esi, [ebp+FAT.SECTORS_PER_CLUSTER]
imul esi, [ebp+FAT.BYTES_PER_SECTOR]
push ecx
; find the last cluster of file
mov eax, [edi+20-2]
mov ax, [edi+26]
mov ecx, [edi+28]
jecxz .zero_size
.last_loop:
sub ecx, esi
jbe .last_found
call get_FAT
jnc @f
.device_err:
pop ecx
.device_err2:
pop esi
push ERROR_DEVICE
.ret_err:
pop eax
stc
ret
@@:
cmp eax, 2
jb .fat_err
cmp eax, [ebp+FAT.fatRESERVED]
jb .last_loop
.fat_err:
pop ecx esi
push ERROR_FAT_TABLE
jmp .ret_err
.last_found:
push eax
call get_FAT
jnc @f
pop eax
jmp .device_err
@@:
cmp eax, [ebp+FAT.fatRESERVED]
pop eax
jb .fat_err
; set length to full number of clusters
sub [edi+28], ecx
.start_extend:
pop ecx
; now do extend
push edx
mov edx, 2 ; start scan from cluster 2
.extend_loop:
cmp [edi+28], ecx
jae .extend_done
; add new cluster
push eax
call get_free_FAT
jc .disk_full
mov edx, [ebp+FAT.fatEND]
call set_FAT
mov edx, eax
pop eax
test eax, eax
jz .first_cluster
push edx
call set_FAT
pop edx
jmp @f
.first_cluster:
ror edx, 16
mov [edi+20], dx
ror edx, 16
mov [edi+26], dx
@@:
push ecx
mov ecx, -1
call add_disk_free_space
pop ecx
mov eax, edx
add [edi+28], esi
jmp .extend_loop
.extend_done:
mov [edi+28], ecx
pop edx esi
xor eax, eax ; CF=0
ret
.device_err3:
pop edx
jmp .device_err2
.disk_full:
pop eax edx esi
movi eax, ERROR_DISK_FULL
stc
ret
 
fat_update_datetime:
call get_time_for_file
mov [edi+22], ax ; last write time
call get_date_for_file
mov [edi+24], ax ; last write date
mov [edi+18], ax ; last access date
ret
 
 
;----------------------------------------------------------------
; fat_SetFileEnd - FAT implementation of setting end-of-file
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_SetFileEnd:
call fat_lock
push edi
cmp byte [esi], 0
jnz @f
.access_denied:
push ERROR_ACCESS_DENIED
.ret:
call fat_unlock
pop eax
pop edi
ret
@@:
stdcall hd_find_lfn, [esp+4+4]
jnc @f
.reteax:
push eax
jmp .ret
@@:
; must not be directory
test byte [edi+11], 10h
jnz .access_denied
; file size must not exceed 4 Gb
cmp dword [ebx+8], 0
jz @f
push ERROR_END_OF_FILE
jmp .ret
@@:
push eax ; save directory sector
; set file modification date/time to current
call fat_update_datetime
mov eax, [ebx+4]
cmp eax, [edi+28]
jb .truncate
ja .expand
pop eax
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
test eax, eax
jz @f
push ERROR_DEVICE
jmp .ret
@@:
push 0
jmp .ret
.expand:
push ebx ebp ecx
push dword [edi+28] ; save old size
mov ecx, eax
call hd_extend_file
push eax ; return code
jnc .expand_ok
cmp al, ERROR_DISK_FULL
jz .disk_full
.pop_ret:
call update_disk
pop eax ecx ecx ebp ebx ecx
jmp .reteax
.expand_ok:
.disk_full:
; save directory
mov eax, [edi+28]
xchg eax, [esp+20]
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
test eax, eax
mov eax, [edi+20-2]
mov ax, [edi+26]
mov edi, eax
jz @f
.pop_ret11:
mov byte [esp], ERROR_DEVICE
jmp .pop_ret
@@:
test edi, edi
jz .pop_ret
; now zero new data
push 0
; edi=current cluster, [esp]=sector in cluster
; [esp+24]=new size, [esp+8]=old size, [esp+4]=return code
.zero_loop:
cmp edi, 2
jb .error_fat
cmp edi, [ebp+FAT.fatRESERVED]
jae .error_fat
sub dword [esp+8], 0x200
jae .next_cluster
lea eax, [edi-2]
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
add eax, [esp]
cmp dword [esp+8], -0x200
jz .noread
push eax
lea ebx, [ebp+FAT.buffer]
call fs_read32_app
test eax, eax
pop eax
jnz .err_next
.noread:
mov ecx, [esp+8]
neg ecx
push edi
lea edi, [ebp+FAT.buffer+0x200]
add edi, [esp+12]
push eax
xor eax, eax
mov [esp+16], eax
rep stosb
pop eax
pop edi
call fs_write32_app
test eax, eax
jz .next_cluster
.err_next:
mov byte [esp+4], ERROR_DEVICE
.next_cluster:
pop eax
sub dword [esp+20], 0x200
jbe .pop_ret
inc eax
push eax
cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER]
jb .zero_loop
and dword [esp], 0
mov eax, edi
call get_FAT
mov edi, eax
jnc .zero_loop
pop eax
jmp .pop_ret11
.truncate:
mov [edi+28], eax
push ecx
mov ecx, [edi+20-2]
mov cx, [edi+26]
push eax
test eax, eax
jz .zero_size
; find new last cluster
@@:
cmp ecx, 2
jb .error_fat2
cmp ecx, [ebp+FAT.fatRESERVED]
jae .error_fat2
mov eax, [ebp+FAT.SECTORS_PER_CLUSTER]
shl eax, 9
sub [esp], eax
jbe @f
mov eax, ecx
call get_FAT
mov ecx, eax
jnc @b
.device_err3:
pop eax ecx eax edi
call update_disk
call fat_unlock
movi eax, ERROR_DEVICE
ret
@@:
; we will zero data at the end of last sector - remember it
push ecx
; terminate FAT chain
push edx
mov eax, ecx
mov edx, [ebp+FAT.fatEND]
call set_FAT
mov eax, edx
pop edx
jnc @f
.device_err4:
pop ecx
jmp .device_err3
.zero_size:
and word [edi+20], 0
and word [edi+26], 0
push 0
mov eax, ecx
@@:
; delete FAT chain
call clear_cluster_chain
jc .device_err4
; save directory
mov eax, [esp+12]
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
pop ebx
test eax, eax
jnz .device_err4
; zero last sector, ignore errors
pop ecx
pop eax
dec ecx
imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
add ecx, [ebp+FAT.DATA_START]
push eax
sar eax, 9
add ecx, eax
pop eax
and eax, 0x1FF
jz .truncate_done
push ebx eax
mov eax, ecx
lea ebx, [ebp+FAT.buffer]
call fs_read32_app
pop eax
lea edi, [ebp+FAT.buffer+eax]
push ecx
mov ecx, 0x200
sub ecx, eax
xor eax, eax
rep stosb
pop eax
call fs_write32_app
pop ebx
.truncate_done:
pop ecx eax edi
call update_disk
call fat_unlock
xor eax, eax
ret
.error_fat:
pop eax
mov byte [esp], ERROR_FAT_TABLE
jmp .pop_ret
.error_fat2:
pop eax ecx eax edi
call update_disk
call fat_unlock
movi eax, ERROR_FAT_TABLE
ret
 
;----------------------------------------------------------------
; fat_GetFileInfo - FAT implementation of getting file info
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_GetFileInfo:
cmp byte [esi], 0
jnz @f
mov eax, 2
ret
@@:
push edi
call fat_lock
stdcall hd_find_lfn, [esp+4+4]
jc .error
push ebp
xor ebp, ebp
mov esi, [ebx+16]
mov dword [esi+4], ebp
call fat_entry_to_bdfe2
pop ebp
call fat_unlock
xor eax, eax
pop edi
ret
.error:
push eax
call fat_unlock
pop eax
pop edi
ret
 
;----------------------------------------------------------------
; fat_SetFileInfo - FAT implementation of setting file info
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_SetFileInfo:
cmp byte [esi], 0
jnz @f
mov eax, 2
ret
@@:
push edi
call fat_lock
stdcall hd_find_lfn, [esp+4+4]
jc .error
push eax
mov edx, [ebx+16]
call bdfe_to_fat_entry
pop eax
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
call update_disk
call fat_unlock
pop edi
xor eax, eax
ret
.error:
push eax
call fat_unlock
pop eax
pop edi
ret
 
;----------------------------------------------------------------
; fat_Delete - FAT implementation of deleting a file/folder
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_Delete:
call fat_lock
cmp byte [esi], 0
jnz @f
; cannot delete root!
.access_denied:
push ERROR_ACCESS_DENIED
.pop_ret:
call fat_unlock
pop eax
xor ebx, ebx
ret
@@:
and [ebp+FAT.longname_sec1], 0
and [ebp+FAT.longname_sec2], 0
push edi
stdcall hd_find_lfn, [esp+4+4]
jnc .found
pop edi
push ERROR_FILE_NOT_FOUND
jmp .pop_ret
.found:
cmp dword [edi], '. '
jz .access_denied2
cmp dword [edi], '.. '
jz .access_denied2
test byte [edi+11], 10h
jz .dodel
; we can delete only empty folders!
pushad
mov esi, [edi+20-2]
mov si, [edi+26]
xor ecx, ecx
lea eax, [esi-2]
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
lea ebx, [ebp+FAT.buffer]
call fs_read32_sys
test eax, eax
jnz .err1
lea eax, [ebx+0x200]
add ebx, 2*0x20
.checkempty:
cmp byte [ebx], 0
jz .empty
cmp byte [ebx], 0xE5
jnz .notempty
add ebx, 0x20
cmp ebx, eax
jb .checkempty
inc ecx
cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
jb @f
mov eax, esi
call get_FAT
jc .err1
cmp eax, 2
jb .error_fat
cmp eax, [ebp+FAT.fatRESERVED]
jae .empty
mov esi, eax
xor ecx, ecx
@@:
lea eax, [esi-2]
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
add eax, ecx
lea ebx, [ebp+FAT.buffer]
call fs_read32_sys
test eax, eax
lea eax, [ebx+0x200]
jz .checkempty
.err1:
popad
.err2:
pop edi
call fat_unlock
movi eax, ERROR_DEVICE
ret
.error_fat:
popad
pop edi
call fat_unlock
movi eax, ERROR_FAT_TABLE
ret
.notempty:
popad
.access_denied2:
pop edi
call fat_unlock
movi eax, ERROR_ACCESS_DENIED
ret
.empty:
popad
push eax ebx
lea ebx, [ebp+FAT.buffer]
call fs_read32_sys
test eax, eax
pop ebx eax
jnz .err2
.dodel:
push eax
mov eax, [edi+20-2]
mov ax, [edi+26]
xchg eax, [esp]
; delete folder entry
mov byte [edi], 0xE5
; delete LFN (if present)
.lfndel:
lea edx, [ebp+FAT.buffer]
cmp edi, edx
ja @f
cmp [ebp+FAT.longname_sec2], 0
jz .lfndone
push [ebp+FAT.longname_sec2]
push [ebp+FAT.longname_sec1]
pop [ebp+FAT.longname_sec2]
and [ebp+FAT.longname_sec1], 0
push ebx
mov ebx, edx
call fs_write32_sys
mov eax, [esp+4]
call fs_read32_sys
pop ebx
pop eax
lea edi, [ebp+FAT.buffer+0x200]
@@:
sub edi, 0x20
cmp byte [edi], 0xE5
jz .lfndone
cmp byte [edi+11], 0xF
jnz .lfndone
mov byte [edi], 0xE5
jmp .lfndel
.lfndone:
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
pop ebx
; delete FAT chain
pop eax
call clear_cluster_chain
call update_disk
call fat_unlock
pop edi
xor eax, eax
ret
 
; \end{diamond}
/kernel/branches/Kolibri-acpi/fs/fs_lfn.inc
30,18 → 30,6
iglobal
; in this table names must be in lowercase
rootdirs:
db 2,'rd'
dd fs_OnRamdisk
dd fs_NextRamdisk
db 7,'ramdisk'
dd fs_OnRamdisk
dd fs_NextRamdisk
db 2,'fd'
dd fs_OnFloppy
dd fs_NextFloppy
db 10,'floppydisk'
dd fs_OnFloppy
dd fs_NextFloppy
;**********************************************
db 3,'cd0'
dd fs_OnCd0
60,10 → 48,6
 
 
virtual_root_query:
dd fs_HasRamdisk
db 'rd',0
dd fs_HasFloppy
db 'fd',0
;**********************************************
dd fs_HasCd0
db 'cd0',0
75,12 → 59,6
db 'cd3',0
;**********************************************
dd 0
 
fs_additional_handlers:
dd dyndisk_handler, dyndisk_enum_root
; add new handlers here
dd 0
 
endg
 
file_system_lfn_protected:
257,7 → 235,6
 
.readroot:
; virtual root folder - special handler
mov esi, virtual_root_query
mov ebp, [ebx+12]
mov edx, [ebx+16]
; add edx, std_application_base_address
269,9 → 246,51
mov ecx, 32/4
rep stosd
mov byte [edx], 1 ; version
sub esp, 16
.readroot_ah_loop2:
push edi
lea edi, [esp+4]
call dyndisk_enum_root
pop edi
test eax, eax
jz .readroot_done_dynamic
inc dword [edx+8]
dec dword [esp+16]
jns .readroot_ah_loop2
dec ebp
js .readroot_ah_loop2
push eax
xor eax, eax
inc dword [edx+4]
mov dword [edi], 0x10 ; attributes: folder
mov dword [edi+4], ebx
add edi, 8
mov ecx, 40/4-2
rep stosd
push esi edi
lea esi, [esp+12]
@@:
lodsb
stosb
test bl, 1
jz .ansi3
mov byte [edi], 0
inc edi
.ansi3:
test al, al
jnz @b
pop edi esi eax
add edi, 520
test bl, 1
jnz .readroot_ah_loop2
sub edi, 520-264
jmp .readroot_ah_loop2
.readroot_done_dynamic:
add esp, 16
mov esi, virtual_root_query
.readroot_loop:
cmp dword [esi], eax
jz .readroot_done_static
jz .readroot_done
call dword [esi]
add esi, 4
test eax, eax
312,54 → 331,7
jnz .readroot_loop
sub edi, 520-264
jmp .readroot_loop
.readroot_done_static:
mov esi, fs_additional_handlers-8
sub esp, 16
.readroot_ah_loop:
add esi, 8
cmp dword [esi], 0
jz .readroot_done
xor eax, eax
.readroot_ah_loop2:
push edi
lea edi, [esp+4]
call dword [esi+4]
pop edi
test eax, eax
jz .readroot_ah_loop
inc dword [edx+8]
dec dword [esp+16]
jns .readroot_ah_loop2
dec ebp
js .readroot_ah_loop2
push eax
xor eax, eax
inc dword [edx+4]
mov dword [edi], 0x10 ; attributes: folder
mov dword [edi+4], ebx
add edi, 8
mov ecx, 40/4-2
rep stosd
push esi edi
lea esi, [esp+12]
@@:
lodsb
stosb
test bl, 1
jz .ansi3
mov byte [edi], 0
inc edi
.ansi3:
test al, al
jnz @b
pop edi esi eax
add edi, 520
test bl, 1
jnz .readroot_ah_loop2
sub edi, 520-264
jmp .readroot_ah_loop2
.readroot_done:
add esp, 16
pop eax
mov ebx, [edx+4]
xor eax, eax
371,14 → 343,7
mov [image_of_ebx], ebx
ret
.notfound_try:
mov edi, fs_additional_handlers
@@:
cmp dword [edi], 0
jz .notfound
call dword [edi]
scasd
scasd
jmp @b
call dyndisk_handler
.notfound:
mov dword [image_of_eax], ERROR_FILE_NOT_FOUND
and dword [image_of_ebx], 0
436,72 → 401,10
; ebp = 0 or pointer to rest of name from folder addressed by esi
; out: [image_of_eax]=image of eax, [image_of_ebx]=image of ebx
 
fs_OnRamdisk:
cmp ecx, 1
jnz file_system_lfn.notfound
mov eax, [ebx]
cmp eax, fs_NumRamdiskServices
jae .not_impl
mov ecx, [ebx+12]
mov edx, [ebx+16]
; add edx, std_application_base_address
add ebx, 4
call dword [fs_RamdiskServices + eax*4]
mov [image_of_eax], eax
mov [image_of_ebx], ebx
ret
.not_impl:
mov dword [image_of_eax], 2 ; not implemented
ret
 
fs_NotImplemented:
mov eax, 2
ret
 
fs_RamdiskServices:
dd fs_RamdiskRead
dd fs_RamdiskReadFolder
dd fs_RamdiskRewrite
dd fs_RamdiskWrite
dd fs_RamdiskSetFileEnd
dd fs_RamdiskGetFileInfo
dd fs_RamdiskSetFileInfo
dd 0
dd fs_RamdiskDelete
dd fs_RamdiskCreateFolder
fs_NumRamdiskServices = ($ - fs_RamdiskServices)/4
 
fs_OnFloppy:
cmp ecx, 2
ja file_system_lfn.notfound
mov eax, [ebx]
cmp eax, fs_NumFloppyServices
jae fs_OnRamdisk.not_impl
call reserve_flp
mov [flp_number], cl
mov ecx, [ebx+12]
mov edx, [ebx+16]
; add edx, std_application_base_address
add ebx, 4
call dword [fs_FloppyServices + eax*4]
and [flp_status], 0
mov [image_of_eax], eax
mov [image_of_ebx], ebx
ret
 
fs_FloppyServices:
dd fs_FloppyRead
dd fs_FloppyReadFolder
dd fs_FloppyRewrite
dd fs_FloppyWrite
dd fs_FloppySetFileEnd
dd fs_FloppyGetFileInfo
dd fs_FloppySetFileInfo
dd 0
dd fs_FloppyDelete
dd fs_FloppyCreateFolder
fs_NumFloppyServices = ($ - fs_FloppyServices)/4
 
;*******************************************************
fs_OnCd0:
call reserve_cd
584,16 → 487,6
fs_NumCdServices = ($ - fs_CdServices)/4
 
;*******************************************************
 
fs_HasRamdisk:
mov al, 1 ; we always have ramdisk
ret
fs_HasFloppy:
cmp byte [DRIVE_DATA], 0
setnz al
ret
 
;*******************************************************
fs_HasCd0:
test byte [DRIVE_DATA+1], 10000000b
setnz al
617,36 → 510,6
; out: CF=1 => no more partitions
; CF=0 => eax=next partition number
 
fs_NextRamdisk:
; we always have /rd/1
test eax, eax
stc
jnz @f
mov al, 1
clc
@@:
ret
 
fs_NextFloppy:
; we have /fd/1 iff (([DRIVE_DATA] and 0xF0) != 0) and /fd/2 iff (([DRIVE_DATA] and 0x0F) != 0)
test byte [DRIVE_DATA], 0xF0
jz .no1
test eax, eax
jnz .no1
inc eax
ret ; CF cleared
.no1:
test byte [DRIVE_DATA], 0x0F
jz .no2
cmp al, 2
jae .no2
mov al, 2
clc
ret
.no2:
stc
ret
 
;*******************************************************
fs_NextCd:
; we always have /cdX/1
/kernel/branches/Kolibri-acpi/gui/char2_et.mt
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/kernel/branches/Kolibri-acpi/gui/char_et.mt
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/kernel/branches/Kolibri-acpi/kernel.asm
759,10 → 759,11
; Initialize system timer (IRQ0)
call PIT_init
 
; CALCULATE FAT CHAIN FOR RAMDISK
; Register ramdisk file system
mov esi, boot_initramdisk
call boot_log
call ramdisk_init
 
call calculatefatchain
 
mov esi, boot_initapic
call boot_log
; Try to Initialize APIC
1333,8 → 1334,8
jnz .yes
call stack_handler_has_work?
jnz .yes
; call check_fdd_motor_status_has_work?
; jnz .yes
call check_fdd_motor_status_has_work?
jnz .yes
call check_ATAPI_device_event_has_work?
jnz .yes
call check_lights_state_has_work?
2673,29 → 2674,14
align 4
sys_cachetodiskette:
cmp ebx, 1
jne .no_floppy_a_save
mov [flp_number], 1
jmp .save_image_on_floppy
;--------------------------------------
align 4
.no_floppy_a_save:
jb .no_floppy_save
cmp ebx, 2
jne .no_floppy_b_save
mov [flp_number], 2
;--------------------------------------
align 4
.save_image_on_floppy:
ja .no_floppy_save
call save_image
mov [esp + 32], dword 0
cmp [FDC_Status], 0
je .yes_floppy_save
;--------------------------------------
align 4
.no_floppy_b_save:
mov [esp + 32], eax
ret
.no_floppy_save:
mov [esp + 32], dword 1
;--------------------------------------
align 4
.yes_floppy_save:
ret
;------------------------------------------------------------------------------
uglobal
5266,19 → 5252,6
 
align 4
 
syscall_openramdiskfile: ; OpenRamdiskFile
 
mov eax, ebx
mov ebx, ecx
mov ecx, edx
mov edx, esi
mov esi, 12
call fileread
mov [esp+32], eax
ret
 
align 4
 
syscall_drawrect: ; DrawRect
 
mov edi, edx ; color + gradient
5638,13 → 5611,7
; eax - new Screen_Max_X
; ecx - new BytesPerScanLine
; edx - new Screen_Max_Y
cmp eax, [Screen_Max_X]
jne .set
 
cmp edx, [Screen_Max_Y]
jne .set
ret
.set:
pushfd
cli
 
5783,12 → 5750,11
cli
 
if ~ defined extended_primary_loader
mov eax, kernel_file ; load kernel.mnt to 0x7000:0
movi esi, 12
xor ebx, ebx
or ecx, -1
mov edx, OS_BASE+0x70000
call fileread
; load kernel.mnt to 0x7000:0
mov ebx, kernel_file_load
pushad
call file_system_lfn
popad
 
mov esi, restart_kernel_4000+OS_BASE+0x10000 ; move kernel re-starter to 0x4000:0
mov edi, OS_BASE+0x40000
5802,8 → 5768,6
; cld
; rep movsd
 
call restorefatchain
 
call IRQ_mask_all
 
if 0
/kernel/branches/Kolibri-acpi/kernel32.inc
185,11 → 185,9
 
include "blkdev/disk.inc" ; support for plug-n-play disks
include "blkdev/disk_cache.inc" ; caching for plug-n-play disks
include "fs/fs.inc" ; syscall
include "fs/fat32.inc" ; read / write for fat32 filesystem
include "blkdev/rd.inc" ; ramdisk read /write
include "fs/fat.inc" ; read / write for fat filesystem
include "fs/ntfs.inc" ; read / write for ntfs filesystem
include "fs/fat12.inc" ; read / write for fat12 filesystem
include "blkdev/rd.inc" ; ramdisk read /write
include "fs/fs_lfn.inc" ; syscall, version 2
include "fs/iso9660.inc" ; read for iso9660 filesystem CD
include "fs/ext2/ext2.asm" ; read / write for ext2 filesystem
/kernel/branches/Kolibri-acpi/memmap.inc
198,8 → 198,7
; 0x800A0000 -> AFFFF screen access area
; 0x800B0000 -> FFFFF bios rest in peace -area (320k) ?
; 0x80100000 -> 27FFFF diskette image (1m5)
; 0x80280000 -> 281FFF ramdisk fat (8k)
; 0x80282000 -> 283FFF floppy fat (8k)
; 0x80280000 -> 283FFF free (16k)
;
; 0x80284000 -> 28BFFF HDD DMA AREA (32k)
; 0x8028C000 -> 297FFF free (48k)
/kernel/branches/Kolibri-acpi/readme-ext-loader.txt
0,0 → 1,52
При компиляции ядра можно задать - например, в lang.inc, - дополнительный
параметр extended_primary_loader=1; он переключает ядро на альтернативный
способ загрузки. Загрузка несовместима
с основой версией ядра; требуется специальный первичный загрузчик, существующие
собраны в папке bootloader/extended_primary_loader.
Есть варианты загрузки с FAT12/FAT16/FAT32/ISO,
есть вариант загрузчика, встраивающегося в загрузку Windows. Встраивание
в GRUB аналогично описанному для основного способа загрузки -
последним загрузчиком в цепочке
при этом оказывается тот, который установлен в образе дискеты FAT12.
 
При загрузке поддерживается опрос параметров из файла config.ini,
но не поддерживается сохранение выбранных параметров. Файл config.ini
ищется рядом с первичным загрузчиком, как и ядро kernel.mnt; в случае
загрузчика с дискеты эти файлы располагаются на самой дискете,
в случае других загрузчиков - рядом с первичным загрузчиком вне образа.
 
Если config.ini не найден, используются умолчальные значения. Если
config.ini найден, то он разбивается на строчки, строчки должны иметь
вид <параметр>=<значение>, перед параметром и вокруг знака равенства
могут быть пробелы, всё, что идёт в строке после значения, игнорируется.
Параметры чувствительны к регистру символов.
Строки, не имеющие такого вида, а также строки, в которых параметр неизвестен,
а также строки, в которых значение недопустимо, игнорируются.
 
Все числа должны быть целыми неотрицательными, записанными в десятичной
системе счисления. Булевские значения кодируются следующим образом:
0=off=no соответствует выключенному параметру, 1=on=yes - включённому.
 
Известные параметры:
 
timeout=<число секунд> задаёт время ожидания в экране выбора параметров.
Если таймаут больше 9, используется значение 9. Значение по умолчанию 5.
 
resolution=<ширина>*<высота> или <ширина>x<высота> задаёт желаемое
разрешение графического режима. Если такого графического режима,
устраивающего систему, не найдено, параметр игнорируется. По умолчанию
пробуются последовательно разрешения 1024*768, 800*600, 640*480.
 
vbemode=<номер видеорежима VBE> задаёт желаемый графический режим.
Если такой режим не существует или не устраивает систему, параметр
игнорируется. Параметр более приоритетен, чем resolution. Умолчального
значения нет.
 
vrr=<включить VRR> - булевский параметр. Умолчальное значение 0.
 
biosdisks=<включить доступ к дискам через BIOS> - булевский параметр.
Умолчальное значение 1.
 
imgfrom=<источник рамдиска>. 1 - грузить дискету, 2 - грузить файл
kolibri.img, находящийся рядом с первичным загрузчиком. Умолчальное
значение 1 при загрузке с дискеты и 2 в противном случае.