/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/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 |
---|
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 в противном случае. |