Subversion Repositories Kolibri OS

Rev

Rev 2150 | Rev 2434 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 2150 Rev 2268
Line 3... Line 3...
3
;; Copyright (C) KolibriOS team 2011. All rights reserved.      ;;
3
;; Copyright (C) KolibriOS team 2011. All rights reserved.      ;;
4
;; Distributed under terms of the GNU General Public License    ;;
4
;; Distributed under terms of the GNU General Public License    ;;
5
;;                                                              ;;
5
;;                                                              ;;
6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Line 7... Line 7...
7
 
7
 
Line 8... Line 8...
8
$Revision: 2140 $
8
$Revision: 2257 $
9
 
9
 
10
; =============================================================================
10
; =============================================================================
11
; ================================= Constants =================================
11
; ================================= Constants =================================
12
; =============================================================================
12
; =============================================================================
13
; Error codes for callback functions.
13
; Error codes for callback functions.
14
DISK_STATUS_OK              = 0 ; success
14
DISK_STATUS_OK		    = 0 ; success
15
DISK_STATUS_GENERAL_ERROR   = -1; if no other code is suitable
15
DISK_STATUS_GENERAL_ERROR   = -1; if no other code is suitable
16
DISK_STATUS_INVALID_CALL    = 1 ; invalid input parameters
16
DISK_STATUS_INVALID_CALL    = 1 ; invalid input parameters
17
DISK_STATUS_NO_MEDIA        = 2 ; no media present
17
DISK_STATUS_NO_MEDIA	    = 2 ; no media present
18
DISK_STATUS_END_OF_MEDIA    = 3 ; end of media while reading/writing data
18
DISK_STATUS_END_OF_MEDIA    = 3 ; end of media while reading/writing data
19
; Driver flags. Represent bits in DISK.DriverFlags.
19
; Driver flags. Represent bits in DISK.DriverFlags.
20
DISK_NO_INSERT_NOTIFICATION = 1
20
DISK_NO_INSERT_NOTIFICATION = 1
Line 21... Line 21...
21
; Media flags. Represent bits in DISKMEDIAINFO.Flags.
21
; Media flags. Represent bits in DISKMEDIAINFO.Flags.
22
DISK_MEDIA_READONLY = 1
22
DISK_MEDIA_READONLY = 1
23
 
23
 
24
; If we see too many partitions, probably there is some error on the disk.
24
; If too many partitions are detected,there is probably an error on the disk.
25
; 256 partitions should be enough for any reasonable use.
25
; 256 partitions should be enough for any reasonable use.
Line 26... Line 26...
26
; Also, the same number is limiting the number of MBRs to process; if we see
26
; Also, the same number is limiting the number of MBRs to process; if
27
; too many MBRs, probably there is a loop in the MBR structure.
27
; too many MBRs are visible,there probably is a loop in the MBR structure.
28
MAX_NUM_PARTITIONS = 256
28
MAX_NUM_PARTITIONS = 256
29
 
29
 
30
; =============================================================================
30
; =============================================================================
31
; ================================ Structures =================================
31
; ================================ Structures =================================
32
; =============================================================================
32
; =============================================================================
33
; This structure defines all callback functions for working with the physical
33
; This structure defines all callback functions for working with the physical
34
; device. They are implemented by a driver. Objects with this structure reside
34
; device. They are implemented by a driver. Objects with this structure reside
35
; in a driver.
35
; in a driver.
36
struct DISKFUNC
36
struct DISKFUNC
37
.strucsize      dd      ?
37
.strucsize	dd	?
38
; Size of the structure. This field is intended for possible extensions of
38
; Size of the structure. This field is intended for possible extensions of
39
; this structure. If a new function is added to this structure and a driver
39
; this structure. If a new function is added to this structure and a driver
40
; implements an old version, the caller can detect this by checking .strucsize,
40
; implements an old version, the caller can detect this by checking .strucsize,
41
; so the driver remains compatible.
41
; so the driver remains compatible.
42
.close          dd      ?
42
.close		dd	?
43
; The pointer to the function which frees all driver-specific resources for
43
; The pointer to the function which frees all driver-specific resources for
44
; the disk.
44
; the disk.
45
; Optional, may be NULL.
45
; Optional, may be NULL.
46
; void close(void* userdata);
46
; void close(void* userdata);
47
.closemedia     dd      ?
47
.closemedia	dd	?
48
; The pointer to the function which informs the driver that the kernel has
48
; The pointer to the function which informs the driver that the kernel has
49
; finished all processing with the current media. If media is removed, the
49
; finished all processing with the current media. If media is removed, the
50
; driver should decline all requests to that media with DISK_STATUS_NO_MEDIA,
50
; driver should decline all requests to that media with DISK_STATUS_NO_MEDIA,
51
; even if new media is inserted, until this function is called. If media is
51
; even if new media is inserted, until this function is called. If media is
52
; removed, a new call to 'disk_media_changed' is not allowed until this
52
; removed, a new call to 'disk_media_changed' is not allowed until this
53
; function is called.
53
; function is called.
54
; Optional, may be NULL (if media is not removable).
54
; Optional, may be NULL (if media is not removable).
55
; void closemedia(void* userdata);
55
; void closemedia(void* userdata);
56
.querymedia     dd      ?
56
.querymedia	dd	?
57
; The pointer to the function which determines capabilities of the media.
57
; The pointer to the function which determines capabilities of the media.
58
; int querymedia(void* userdata, DISKMEDIAINFO* info);
58
; int querymedia(void* userdata, DISKMEDIAINFO* info);
59
; Return value: one of DISK_STATUS_*
59
; Return value: one of DISK_STATUS_*
60
.read           dd      ?
60
.read		dd	?
61
; The pointer to the function which reads data from the device.
61
; The pointer to the function which reads data from the device.
62
; int read(void* userdata, void* buffer, __int64 startsector, int* numsectors);
62
; int read(void* userdata, void* buffer, __int64 startsector, int* numsectors);
63
; input: *numsectors = number of sectors to read
63
; input: *numsectors = number of sectors to read
64
; output: *numsectors = number of sectors which were successfully read
64
; output: *numsectors = number of sectors which were successfully read
65
; Return value: one of DISK_STATUS_*
65
; Return value: one of DISK_STATUS_*
66
.write          dd      ?
66
.write		dd	?
67
; The pointer to the function which writes data to the device.
67
; The pointer to the function which writes data to the device.
68
; Optional, may be NULL.
68
; Optional, may be NULL.
69
; int write(void* userdata, void* buffer, __int64 startsector, int* numsectors);
69
; int write(void* userdata, void* buffer, __int64 startsector, int* numsectors);
70
; input: *numsectors = number of sectors to write
70
; input: *numsectors = number of sectors to write
71
; output: *numsectors = number of sectors which were successfully written
71
; output: *numsectors = number of sectors which were successfully written
72
; Return value: one of DISK_STATUS_*
72
; Return value: one of DISK_STATUS_*
73
.flush          dd      ?
73
.flush		dd	?
74
; The pointer to the function which flushes the internal device cache.
74
; The pointer to the function which flushes the internal device cache.
75
; Optional, may be NULL.
75
; Optional, may be NULL.
76
; int flush(void* userdata);
76
; int flush(void* userdata);
77
; Return value: one of DISK_STATUS_*
77
; Return value: one of DISK_STATUS_*
78
; Note that read/write are called by the cache manager, so a driver should not
78
; Note that read/write are called by the cache manager, so a driver should not
79
; create a software cache. This function is implemented for flushing a hardware
79
; create a software cache. This function is implemented for flushing a hardware
80
; cache, if it exists.
80
; cache, if it exists.
81
.adjust_cache_size      dd      ?
81
.adjust_cache_size	dd	?
82
; The pointer to the function which returns the cache size for this device.
82
; The pointer to the function which returns the cache size for this device.
Line 83... Line 83...
83
; Optional, may be NULL.
83
; Optional, may be NULL.
84
; unsigned int adjust_cache_size(unsigned int suggested_size);
84
; unsigned int adjust_cache_size(unsigned int suggested_size);
85
; Return value: 0 = disable cache, otherwise = used cache size in bytes.
85
; Return value: 0 = disable cache, otherwise = used cache size in bytes.
86
ends
86
ends
87
 
87
 
88
; This structure holds an information about a media.
88
; This structure holds information on a medium.
89
; Objects with this structure are allocated by the kernel as a part of DISK
89
; Objects with this structure are allocated by the kernel as a part of the DISK
90
; structure and filled by a driver in the 'querymedia' callback.
90
; structure and are filled by a driver in the 'querymedia' callback.
91
struct DISKMEDIAINFO
91
struct DISKMEDIAINFO
92
.Flags          dd      ?
92
.Flags		dd	?
93
; Combination of DISK_MEDIA_* bits.
93
; Combination of DISK_MEDIA_* bits.
Line 94... Line 94...
94
.SectorSize     dd      ?
94
.SectorSize	dd	?
95
; Size of the sector.
95
; Size of the sector.
96
.Capacity       dq      ?
96
.Capacity	dq	?
97
; Size of the media in sectors.
97
; Size of the media in sectors.
98
ends
98
ends
99
 
99
 
100
; This structure represents disk cache. To follow the old implementation,
100
; This structure represents the disk cache. To follow the old implementation,
101
; there are two distinct caches for a disk, one for "system" data, other
101
; there are two distinct caches for a disk, one for "system" data,and the other
102
; for "application" data.
102
; for "application" data.
103
struct DISKCACHE
103
struct DISKCACHE
104
.Lock           MUTEX
104
.Lock		MUTEX
105
; Lock to protect the cache.
105
; Lock to protect the cache.
106
; The following fields are inherited from data32.inc:cache_ideX.
106
; The following fields are inherited from data32.inc:cache_ideX.
Line 107... Line 107...
107
.pointer     rd 1
107
.pointer     rd 1
108
.data_size   rd 1   ; not use
108
.data_size   rd 1   ; not use
109
.data        rd 1
109
.data	     rd 1
110
.sad_size    rd 1
110
.sad_size    rd 1
111
.search_start       rd 1
111
.search_start	    rd 1
112
ends
112
ends
113
 
113
 
114
; This structure represents a disk device and its media for the kernel.
114
; This structure represents a disk device and its media for the kernel.
115
; This structure is allocated by the kernel in the 'disk_add' function,
115
; This structure is allocated by the kernel in the 'disk_add' function,
116
; freed in the 'disk_dereference' function.
116
; freed in the 'disk_dereference' function.
117
struct DISK
117
struct DISK
118
; Fields of disk object
118
; Fields of disk object
119
.Next           dd      ?
119
.Next		dd	?
120
.Prev           dd      ?
120
.Prev		dd	?
121
; All disk devices are linked in one list with these two fields.
121
; All disk devices are linked in one list with these two fields.
122
; Head of the list is the 'disk_list' variable.
122
; Head of the list is the 'disk_list' variable.
123
.Functions      dd      ?
123
.Functions	dd	?
124
; Pointer to the 'DISKFUNC' structure with driver functions.
124
; Pointer to the 'DISKFUNC' structure with driver functions.
125
.Name           dd      ?
125
.Name		dd	?
126
; Pointer to the string used for accesses through the global filesystem.
126
; Pointer to the string used for accesses through the global filesystem.
127
.UserData       dd      ?
127
.UserData	dd	?
128
; This field is passed to all callback functions so a driver can decide which
128
; This field is passed to all callback functions so a driver can decide which
129
; physical device is addressed.
129
; physical device is addressed.
130
.DriverFlags    dd      ?
130
.DriverFlags	dd	?
131
; Bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit is defined.
131
; Bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit is defined.
132
; If it is set, the driver will never issue 'disk_media_changed' notification
132
; If it is set, the driver will never issue 'disk_media_changed' notification
133
; with argument set to true, so the kernel must try to detect media during
133
; with argument set to true, so the kernel must try to detect media during
134
; requests from the file system.
134
; requests from the file system.
135
.RefCount       dd      ?
135
.RefCount	dd	?
136
; Count of active references to this structure. One reference is kept during
136
; Count of active references to this structure. One reference is kept during
137
; the lifetime of the structure between 'disk_add' and 'disk_del'.
137
; the lifetime of the structure between 'disk_add' and 'disk_del'.
138
; Another reference is taken during any filesystem operation for this disk.
138
; Another reference is taken during any filesystem operation for this disk.
139
; One reference is added if media is inserted.
139
; One reference is added if media is inserted.
140
; The structure is destroyed when the reference count decrements to zero:
140
; The structure is destroyed when the reference count decrements to zero:
141
; this usually occurs in 'disk_del', but can be delayed to the end of last
141
; this usually occurs in 'disk_del', but can be delayed to the end of last
142
; filesystem operation, if one is active.
142
; filesystem operation, if one is active.
143
.MediaLock              MUTEX
143
.MediaLock		MUTEX
144
; Lock to protect the MEDIA structure. See the description after
144
; Lock to protect the MEDIA structure. See the description after
145
; 'disk_list_mutex' for the locking strategy.
145
; 'disk_list_mutex' for the locking strategy.
146
; Fields of media object
146
; Fields of media object
147
.MediaInserted          db      ?
147
.MediaInserted		db	?
148
; 0 if media is not inserted, nonzero otherwise.
148
; 0 if media is not inserted, nonzero otherwise.
149
.MediaUsed              db      ?
149
.MediaUsed		db	?
150
; 0 if media fields are not used, nonzero otherwise. If .MediaRefCount is
150
; 0 if media fields are not used, nonzero otherwise. If .MediaRefCount is
151
; nonzero, this field is nonzero too; however, when .MediaRefCount goes
151
; nonzero, this field is nonzero too; however, when .MediaRefCount goes
152
; to zero, there is some time interval during which media object is still used.
152
; to zero, there is some time interval during which media object is still used.
153
                align 4
153
		align 4
154
; The following fields are not valid unless either .MediaInserted is nonzero
154
; The following fields are not valid unless either .MediaInserted is nonzero
155
; or they are accessed from a code which has obtained the reference when
155
; or they are accessed from a code which has obtained the reference when
156
; .MediaInserted was nonzero.
156
; .MediaInserted was nonzero.
157
.MediaRefCount          dd      ?
157
.MediaRefCount		dd	?
158
; Count of active references to the media object. One reference is kept during
158
; Count of active references to the media object. One reference is kept during
159
; the lifetime of the media between two calls to 'disk_media_changed'.
159
; the lifetime of the media between two calls to 'disk_media_changed'.
160
; Another reference is taken during any filesystem operation for this media.
160
; Another reference is taken during any filesystem operation for this media.
161
; The callback 'closemedia' is called when the reference count decrements to
161
; The callback 'closemedia' is called when the reference count decrements to
162
; zero: this usually occurs in 'disk_media_changed', but can be delayed to the
162
; zero: this usually occurs in 'disk_media_changed', but can be delayed to the
163
; end of last filesystem operation, if one is active.
163
; end of the last filesystem operation, if one is active.
164
.MediaInfo              DISKMEDIAINFO
164
.MediaInfo		DISKMEDIAINFO
165
; This field keeps an information about the current media.
165
; This field keeps information on the current media.
166
.NumPartitions  dd      ?
166
.NumPartitions	dd	?
167
; Number of partitions on this media.
167
; Number of partitions on this media.
168
.Partitions     dd      ?
168
.Partitions	dd	?
Line 169... Line 169...
169
; Pointer to array of .NumPartitions pointers to PARTITION structures.
169
; Pointer to array of .NumPartitions pointers to PARTITION structures.
170
.cache_size     dd      ?
170
.cache_size	dd	?
171
; inherited from cache_ideX_size
171
; inherited from cache_ideX_size
172
.SysCache       DISKCACHE
172
.SysCache	DISKCACHE
173
.AppCache       DISKCACHE
173
.AppCache	DISKCACHE
174
; Two caches for the disk.
174
; Two caches for the disk.
175
ends
175
ends
176
 
176
 
177
; This structure represents one partition for the kernel. This is a base
177
; This structure represents one partition for the kernel. This is a base
178
; template, the actual contents after common fields is determined by the
178
; template, the actual contents after common fields is determined by the
179
; file system code for this partition.
179
; file system code for this partition.
180
struct PARTITION
180
struct PARTITION
181
.FirstSector    dq      ?
181
.FirstSector	dq	?
182
; First sector of the partition.
182
; First sector of the partition.
183
.Length         dq      ?
183
.Length 	dq	?
184
; Length of the partition in sectors.
184
; Length of the partition in sectors.
185
.Disk           dd      ?
185
.Disk		dd	?
Line 186... Line 186...
186
; Pointer to parent DISK structure.
186
; Pointer to parent DISK structure.
187
.FSUserFunctions        dd      ?
187
.FSUserFunctions	dd	?
188
; Handlers for the sysfunction 70h. This field is a pointer to the following
188
; Handlers for the sysfunction 70h. This field is a pointer to the following
189
; array. The first dword is a number of supported subfunctions, other dwords
189
; array. The first dword is a number of supported subfunctions, other dwords
190
; point to handlers of corresponding subfunctions.
190
; point to handlers of corresponding subfunctions.
191
; This field is 0 if file system is not recognized.
191
; This field is 0 if file system is not recognized.
192
; ...fs-specific data may follow...
192
; ...fs-specific data may follow...
193
ends
193
ends
194
 
194
 
195
; This is an external structure, it represents an entry in the partition table.
195
; This is an external structure, it represents an entry in the partition table.
196
struct PARTITION_TABLE_ENTRY
196
struct PARTITION_TABLE_ENTRY
197
.Bootable       db      ?
197
.Bootable	db	?
198
; 80h = bootable partition, 0 = non-bootable partition, other values = invalid
198
; 80h = bootable partition, 0 = non-bootable partition, other values = invalid
199
.FirstHead      db      ?
199
.FirstHead	db	?
200
.FirstSector    db      ?
200
.FirstSector	db	?
201
.FirstTrack     db      ?
201
.FirstTrack	db	?
202
; Coordinates of first sector in CHS.
202
; Coordinates of first sector in CHS.
203
.Type           db      ?
203
.Type		db	?
204
; Partition type, one of predefined constants. 0 = empty, several types denote
204
; Partition type, one of predefined constants. 0 = empty, several types denote
205
; extended partition (see process_partition_table_entry), we are not interested
205
; extended partition (see process_partition_table_entry), we are not interested
206
; in other values.
206
; in other values.
Line 207... Line 207...
207
.LastHead       db      ?
207
.LastHead	db	?
208
.LastSector     db      ?
208
.LastSector	db	?
209
.LastTrack      db      ?
209
.LastTrack	db	?
210
; Coordinates of last sector in CHS.
210
; Coordinates of last sector in CHS.
211
.FirstAbsSector dd      ?
211
.FirstAbsSector dd	?
212
; Coordinate of first sector in LBA.
212
; Coordinate of first sector in LBA.
213
.Length         dd      ?
213
.Length 	dd	?
214
; Length of the partition in sectors.
214
; Length of the partition in sectors.
215
ends
215
ends
216
 
216
 
217
; =============================================================================
217
; =============================================================================
218
; ================================ Global data ================================
218
; ================================ Global data ================================
219
; =============================================================================
219
; =============================================================================
220
iglobal
220
iglobal
221
; The pseudo-item for the list of all DISK structures.
221
; The pseudo-item for the list of all DISK structures.
222
; Initialized to the empty list.
222
; Initialized to the empty list.
223
disk_list:
223
disk_list:
224
        dd      disk_list
224
	dd	disk_list
225
        dd      disk_list
225
	dd	disk_list
226
endg
226
endg
227
uglobal
227
uglobal
Line 282... Line 282...
282
; is normally 0 and temporarily can become greater. The function increments
282
; is normally 0 and temporarily can become greater. The function increments
283
; this value. If the resulting value is zero, it uses the buffers and
283
; this value. If the resulting value is zero, it uses the buffers and
284
; decrements the value when the job is done. Otherwise, it immediately
284
; decrements the value when the job is done. Otherwise, it immediately
285
; decrements the value and uses buffers from the heap, allocated in the
285
; decrements the value and uses buffers from the heap, allocated in the
286
; beginning and freed in the end.
286
; beginning and freed in the end.
287
partition_buffer_users  dd      -1
287
partition_buffer_users	dd	-1
288
endg
288
endg
289
uglobal
289
uglobal
290
; The static buffers for MBR, bootsector and fs-temporary sector data.
290
; The static buffers for MBR, bootsector and fs-temporary sector data.
291
align 16
291
align 16
292
mbr_buffer      rb      512
292
mbr_buffer	rb	512
293
bootsect_buffer rb      512
293
bootsect_buffer rb	512
294
fs_tmp_buffer   rb      512
294
fs_tmp_buffer	rb	512
295
endg
295
endg
Line 296... Line 296...
296
 
296
 
297
iglobal
297
iglobal
298
; This is the array of default implementations of driver callbacks.
298
; This is the array of default implementations of driver callbacks.
299
; Same as DRIVERFUNC structure except for the first field; all functions must
299
; Same as DRIVERFUNC structure except for the first field; all functions must
300
; have the default implementations.
300
; have the default implementations.
301
align 4
301
align 4
302
disk_default_callbacks:
302
disk_default_callbacks:
303
        dd      disk_default_close
303
	dd	disk_default_close
304
        dd      disk_default_closemedia
304
	dd	disk_default_closemedia
305
        dd      disk_default_querymedia
305
	dd	disk_default_querymedia
306
        dd      disk_default_read
306
	dd	disk_default_read
307
        dd      disk_default_write
307
	dd	disk_default_write
308
        dd      disk_default_flush
308
	dd	disk_default_flush
309
        dd      disk_default_adjust_cache_size
309
	dd	disk_default_adjust_cache_size
Line 310... Line 310...
310
endg
310
endg
311
 
311
 
312
; =============================================================================
312
; =============================================================================
Line 330... Line 330...
330
; NULL = operation has failed
330
; NULL = operation has failed
331
; non-NULL = handle of the disk. This handle can be used
331
; non-NULL = handle of the disk. This handle can be used
332
; in the operations with other Disk* functions.
332
; in the operations with other Disk* functions.
333
; The handle is the pointer to the internal structure DISK.
333
; The handle is the pointer to the internal structure DISK.
334
disk_add:
334
disk_add:
335
        push    ebx esi         ; save used registers to be stdcall
335
	push	ebx esi 	; save used registers to be stdcall
336
; 1. Allocate the DISK structure.
336
; 1. Allocate the DISK structure.
337
; 1a. Call the heap manager.
337
; 1a. Call the heap manager.
338
        push    sizeof.DISK
338
	push	sizeof.DISK
339
        pop     eax
339
	pop	eax
340
        call    malloc
340
	call	malloc
341
; 1b. Check the result. If allocation failed, return (go to 9) with eax = 0.
341
; 1b. Check the result. If allocation failed, return (go to 9) with eax = 0.
342
        test    eax, eax
342
	test	eax, eax
343
        jz      .nothing
343
	jz	.nothing
344
; 2. Copy disk name to the DISK structure.
344
; 2. Copy the disk name to the DISK structure.
345
; 2a. Get length of the name, including the terminating zero.
345
; 2a. Get length of the name, including the terminating zero.
346
        mov     ebx, [esp+8+8]  ; ebx = pointer to name
346
	mov	ebx, [esp+8+8]	; ebx = pointer to name
347
        push    eax             ; save allocated pointer to DISK
347
	push	eax		; save allocated pointer to DISK
348
        xor     eax, eax        ; the argument of malloc() is in eax
348
	xor	eax, eax	; the argument of malloc() is in eax
349
@@:
349
@@:
350
        inc     eax
350
	inc	eax
351
        cmp     byte [ebx+eax-1], 0
351
	cmp	byte [ebx+eax-1], 0
352
        jnz     @b
352
	jnz	@b
353
; 2b. Call the heap manager.
353
; 2b. Call the heap manager.
354
        call    malloc
354
	call	malloc
355
; 2c. Check the result. If allocation failed, go to 7.
355
; 2c. Check the result. If allocation failed, go to 7.
356
        pop     esi             ; restore allocated pointer to DISK
356
	pop	esi		; restore allocated pointer to DISK
357
        test    eax, eax
357
	test	eax, eax
358
        jz      .free
358
	jz	.free
359
; 2d. Store the allocated pointer to the DISK structure.
359
; 2d. Store the allocated pointer to the DISK structure.
360
        mov     [esi+DISK.Name], eax
360
	mov	[esi+DISK.Name], eax
361
; 2e. Copy the name.
361
; 2e. Copy the name.
362
@@:
362
@@:
363
        mov     dl, [ebx]
363
	mov	dl, [ebx]
364
        mov     [eax], dl
364
	mov	[eax], dl
365
        inc     ebx
365
	inc	ebx
366
        inc     eax
366
	inc	eax
367
        test    dl, dl
367
	test	dl, dl
368
        jnz     @b
368
	jnz	@b
369
; 3. Copy other arguments of the function to the DISK structure.
369
; 3. Copy other arguments of the function to the DISK structure.
370
        mov     eax, [esp+4+8]
370
	mov	eax, [esp+4+8]
371
        mov     [esi+DISK.Functions], eax
371
	mov	[esi+DISK.Functions], eax
372
        mov     eax, [esp+12+8]
372
	mov	eax, [esp+12+8]
373
        mov     [esi+DISK.UserData], eax
373
	mov	[esi+DISK.UserData], eax
374
        mov     eax, [esp+16+8]
374
	mov	eax, [esp+16+8]
375
        mov     [esi+DISK.DriverFlags], eax
375
	mov	[esi+DISK.DriverFlags], eax
376
; 4. Initialize other fields of the DISK structure.
376
; 4. Initialize other fields of the DISK structure.
377
; Media is not inserted, reference counter is 1.
377
; Media is not inserted, reference counter is 1.
378
        lea     ecx, [esi+DISK.MediaLock]
378
	lea	ecx, [esi+DISK.MediaLock]
379
        call    mutex_init
379
	call	mutex_init
380
        xor     eax, eax
380
	xor	eax, eax
381
        mov     dword [esi+DISK.MediaInserted], eax
381
	mov	dword [esi+DISK.MediaInserted], eax
382
        inc     eax
382
	inc	eax
383
        mov     [esi+DISK.RefCount], eax
383
	mov	[esi+DISK.RefCount], eax
384
; The DISK structure is initialized.
384
; The DISK structure is initialized.
385
; 5. Insert the new structure to the global list.
385
; 5. Insert the new structure to the global list.
386
; 5a. Acquire the mutex.
386
; 5a. Acquire the mutex.
387
        mov     ecx, disk_list_mutex
387
	mov	ecx, disk_list_mutex
388
        call    mutex_lock
388
	call	mutex_lock
389
; 5b. Insert item to the tail of double-linked list.
389
; 5b. Insert item to the tail of double-linked list.
390
        mov     edx, disk_list
390
	mov	edx, disk_list
391
        list_add_tail esi, edx     ;esi= new edx= list head
391
	list_add_tail esi, edx	   ;esi= new edx= list head
392
; 5c. Release the mutex.
392
; 5c. Release the mutex.
393
        call    mutex_unlock
393
	call	mutex_unlock
394
; 6. Return with eax = pointer to DISK.
394
; 6. Return with eax = pointer to DISK.
395
        xchg    eax, esi
395
	xchg	eax, esi
396
        jmp     .nothing
396
	jmp	.nothing
397
.free:
397
.free:
398
; Memory allocation for DISK structure succeeded, but for disk name failed.
398
; Memory allocation for DISK structure succeeded, but for disk name failed.
399
; 7. Free the DISK structure.
399
; 7. Free the DISK structure.
400
        xchg    eax, esi
400
	xchg	eax, esi
401
        call    free
401
	call	free
402
; 8. Return with eax = 0.
402
; 8. Return with eax = 0.
403
        xor     eax, eax
403
	xor	eax, eax
404
.nothing:
404
.nothing:
405
; 9. Return.
405
; 9. Return.
406
        pop     esi ebx         ; restore used registers to be stdcall
406
	pop	esi ebx 	; restore used registers to be stdcall
407
        ret     16              ; purge 4 dword arguments to be stdcall
407
	ret	16		; purge 4 dword arguments to be stdcall
Line 408... Line 408...
408
 
408
 
409
; This function deletes a disk device from the global filesystem.
409
; This function deletes a disk device from the global filesystem.
410
; This includes:
410
; This includes:
411
; - removing a media including all partitions;
411
; - removing a media including all partitions;
412
; - deleting this structure from the global filesystem;
412
; - deleting this structure from the global filesystem;
413
; - dereferencing the DISK structure and possibly destroying it.
413
; - dereferencing the DISK structure and possibly destroying it.
414
; Parameters:
414
; Parameters:
415
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure.
415
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure.
416
; Return value: none.
416
; Return value: none.
417
disk_del:
417
disk_del:
418
        push    esi         ; save used registers to be stdcall
418
	push	esi	    ; save used registers to be stdcall
419
; 1. Force media to be removed. If the media is already removed, the
419
; 1. Force media to be removed. If the media is already removed, the
420
; call does nothing.
420
; call does nothing.
421
        mov     esi, [esp+4+8]  ; esi = handle of the disk
421
	mov	esi, [esp+4+8]	; esi = handle of the disk
422
        stdcall disk_media_changed, esi, 0
422
	stdcall disk_media_changed, esi, 0
423
; 2. Delete the structure from the global list.
423
; 2. Delete the structure from the global list.
424
; 2a. Acquire the mutex.
424
; 2a. Acquire the mutex.
425
        mov     ecx, disk_list_mutex
425
	mov	ecx, disk_list_mutex
426
        call    mutex_lock
426
	call	mutex_lock
427
; 2b. Delete item from double-linked list.
427
; 2b. Delete item from double-linked list.
428
        mov     eax, [esi+DISK.Next]
428
	mov	eax, [esi+DISK.Next]
429
        mov     edx, [esi+DISK.Prev]
429
	mov	edx, [esi+DISK.Prev]
430
        mov     [eax+DISK.Prev], edx
430
	mov	[eax+DISK.Prev], edx
431
        mov     [edx+DISK.Next], eax
431
	mov	[edx+DISK.Next], eax
432
; 2c. Release the mutex.
432
; 2c. Release the mutex.
433
        call    mutex_unlock
433
	call	mutex_unlock
434
; 3. The structure still has one reference created in disk_add. Remove this
434
; 3. The structure still has one reference created in disk_add. Remove this
435
; reference. If there are no other references, disk_dereference will free the
435
; reference. If there are no other references, disk_dereference will free the
436
; structure.
436
; structure.
437
        call    disk_dereference
437
	call	disk_dereference
438
; 4. Return.
438
; 4. Return.
439
        pop     esi             ; restore used registers to be stdcall
439
	pop	esi		; restore used registers to be stdcall
Line 440... Line 440...
440
        ret     4               ; purge 1 dword argument to be stdcall
440
	ret	4		; purge 1 dword argument to be stdcall
441
 
441
 
442
; This is an internal function which removes a previously obtained reference
442
; This is an internal function which removes a previously obtained reference
443
; to the disk. If this is the last reference, this function lets the driver
443
; to the disk. If this is the last reference, this function lets the driver
444
; finalize all associated data, and afterwards frees the DISK structure.
444
; finalize all associated data, and afterwards frees the DISK structure.
445
; esi = pointer to DISK structure
445
; esi = pointer to DISK structure
446
disk_dereference:
446
disk_dereference:
447
; 1. Decrement reference counter. Use atomic operation to correctly handle
447
; 1. Decrement reference counter. Use atomic operation to correctly handle
448
; possible simultaneous calls.
448
; possible simultaneous calls.
449
lock    dec     [esi+DISK.RefCount]
449
lock	dec	[esi+DISK.RefCount]
450
; 2. If the result is nonzero, there are other references, so nothing to do.
450
; 2. If the result is nonzero, there are other references, so nothing to do.
451
; In this case, return (go to 4).
451
; In this case, return (go to 4).
452
        jnz     .nothing
452
	jnz	.nothing
453
; 3. If we are here, we just removed the last reference and must destroy the
453
; 3. If we are here, we just removed the last reference and must destroy the
454
; disk object.
454
; disk object.
455
; 3a. Call the driver.
455
; 3a. Call the driver.
456
        mov     al, DISKFUNC.close
456
	mov	al, DISKFUNC.close
457
        stdcall disk_call_driver
457
	stdcall disk_call_driver
458
; 3b. Free the structure.
458
; 3b. Free the structure.
459
        xchg    eax, esi
459
	xchg	eax, esi
460
        call    free
460
	call	free
461
; 4. Return.
461
; 4. Return.
Line 462... Line 462...
462
.nothing:
462
.nothing:
463
        ret
463
	ret
464
 
464
 
465
; This is an internal function which removes a previously obtained reference
465
; This is an internal function which removes a previously obtained reference
466
; to the media. If this is the last reference, this function calls 'closemedia'
466
; to the media. If this is the last reference, this function calls 'closemedia'
467
; callback to signal the driver that the processing has finished and it is safe
467
; callback to signal the driver that the processing has finished and it is safe
468
; to inform about a new media.
468
; to inform about a new media.
469
; esi = pointer to DISK structure
469
; esi = pointer to DISK structure
470
disk_media_dereference:
470
disk_media_dereference:
471
; 1. Decrement reference counter. Use atomic operation to correctly handle
471
; 1. Decrement reference counter. Use atomic operation to correctly handle
472
; possible simultaneous calls.
472
; possible simultaneous calls.
473
lock    dec     [esi+DISK.MediaRefCount]
473
lock	dec	[esi+DISK.MediaRefCount]
474
; 2. If the result is nonzero, there are other references, so nothing to do.
474
; 2. If the result is nonzero, there are other references, so nothing to do.
475
; In this case, return (go to 4).
475
; In this case, return (go to 4).
476
        jnz     .nothing
476
	jnz	.nothing
477
; 3. If we are here, we just removed the last reference and must destroy the
477
; 3. If we are here, we just removed the last reference and must destroy the
478
; media object.
478
; media object.
Line 490... Line 490...
490
;   of kernel that the way is free.
490
;   of kernel that the way is free.
491
; In the first case other parts of the kernel do not use DISK.MediaUsed, so it
491
; In the first case other parts of the kernel do not use DISK.MediaUsed, so it
492
; does not matter when this flag is cleared. In the second case this flag must
492
; does not matter when this flag is cleared. In the second case this flag must
493
; be cleared after all other actions, including call to 'closemedia'.
493
; be cleared after all other actions, including call to 'closemedia'.
494
; 3a. Free all partitions.
494
; 3a. Free all partitions.
495
        push    esi edi
495
	push	esi edi
496
        mov     edi, [esi+DISK.NumPartitions]
496
	mov	edi, [esi+DISK.NumPartitions]
497
        mov     esi, [esi+DISK.Partitions]
497
	mov	esi, [esi+DISK.Partitions]
498
        test    edi, edi
498
	test	edi, edi
499
        jz      .nofree
499
	jz	.nofree
500
.freeloop:
500
.freeloop:
501
        lodsd
501
	lodsd
502
        call    free
502
	call	free
503
        dec     edi
503
	dec	edi
504
        jnz     .freeloop
504
	jnz	.freeloop
505
.nofree:
505
.nofree:
506
        pop     edi esi
506
	pop	edi esi
507
; 3b. Free the cache.
507
; 3b. Free the cache.
508
        call    disk_free_cache
508
	call	disk_free_cache
509
; 3c. Call the driver.
509
; 3c. Call the driver.
510
        mov     al, DISKFUNC.closemedia
510
	mov	al, DISKFUNC.closemedia
511
        stdcall disk_call_driver
511
	stdcall disk_call_driver
512
; 3d. Clear the flag.
512
; 3d. Clear the flag.
513
        mov     [esi+DISK.MediaUsed], 0
513
	mov	[esi+DISK.MediaUsed], 0
514
.nothing:
514
.nothing:
515
        ret
515
	ret
Line 516... Line 516...
516
 
516
 
517
; This function is called by the driver and informs the kernel that the media
517
; This function is called by the driver and informs the kernel that the media
518
; has changed. If the media is non-removable, it is called exactly once
518
; has changed. If the media is non-removable, it is called exactly once
519
; immediately after 'disk_add' and once from 'disk_del'.
519
; immediately after 'disk_add' and once from 'disk_del'.
520
; Parameters:
520
; Parameters:
521
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure.
521
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure.
522
; [esp+8] = new status of the media: zero = no media, nonzero = media inserted.
522
; [esp+8] = new status of the media: zero = no media, nonzero = media inserted.
523
disk_media_changed:
523
disk_media_changed:
524
        push    ebx esi edi             ; save used registers to be stdcall
524
	push	ebx esi edi		; save used registers to be stdcall
525
; 1. Remove the existing media, if it is present.
525
; 1. Remove the existing media, if it is present.
526
        mov     esi, [esp+4+12]         ; esi = pointer to DISK
526
	mov	esi, [esp+4+12] 	; esi = pointer to DISK
527
; 1a. Check whether it is present. Since DISK.MediaInserted is changed only
527
; 1a. Check whether it is present. Since DISK.MediaInserted is changed only
528
; in this function and calls to this function are synchronized, no lock is
528
; in this function and calls to this function are synchronized, no lock is
529
; required for checking.
529
; required for checking.
530
        cmp     [esi+DISK.MediaInserted], 0
530
	cmp	[esi+DISK.MediaInserted], 0
531
        jz      .noremove
531
	jz	.noremove
532
; We really need to remove the media.
532
; We really need to remove the media.
533
; 1b. Acquire mutex.
533
; 1b. Acquire mutex.
534
        lea     ecx, [esi+DISK.MediaLock]
534
	lea	ecx, [esi+DISK.MediaLock]
535
        call    mutex_lock
535
	call	mutex_lock
536
; 1c. Clear the flag.
536
; 1c. Clear the flag.
537
        mov     [esi+DISK.MediaInserted], 0
537
	mov	[esi+DISK.MediaInserted], 0
538
; 1d. Release mutex.
538
; 1d. Release mutex.
539
        call    mutex_unlock
539
	call	mutex_unlock
540
; 1e. Remove the "lifetime" reference and possibly destroy the structure.
540
; 1e. Remove the "lifetime" reference and possibly destroy the structure.
541
        call    disk_media_dereference
541
	call	disk_media_dereference
542
.noremove:
542
.noremove:
543
; 2. Test whether there is new media.
543
; 2. Test whether there is new media.
544
        cmp     dword [esp+8+12], 0
544
	cmp	dword [esp+8+12], 0
545
        jz      .noinsert
545
	jz	.noinsert
546
; Yep, there is.
546
; Yep, there is.
547
; 3. Process the new media. We assume that all media fields are available to
547
; 3. Process the new media. We assume that all media fields are available to
548
; use, see comments in 'disk_media_dereference' (this covers using by previous
548
; use, see comments in 'disk_media_dereference' (this covers using by previous
549
; media referencers) and note that calls to this function are synchronized
549
; media referencers) and note that calls to this function are synchronized
550
; (this covers using by new media referencers).
550
; (this covers using by new media referencers).
551
; 3a. Call the 'querymedia' callback.
551
; 3a. Call the 'querymedia' callback.
552
; .Flags are set to zero for possible future extensions.
552
; .Flags are set to zero for possible future extensions.
553
        lea     edx, [esi+DISK.MediaInfo]
553
	lea	edx, [esi+DISK.MediaInfo]
554
        and     [edx+DISKMEDIAINFO.Flags], 0
554
	and	[edx+DISKMEDIAINFO.Flags], 0
555
        mov     al, DISKFUNC.querymedia
555
	mov	al, DISKFUNC.querymedia
556
        stdcall disk_call_driver, edx
556
	stdcall disk_call_driver, edx
557
; 3b. Check the result of the callback. Abort if it failed.
557
; 3b. Check the result of the callback. Abort if it failed.
558
        test    eax, eax
558
	test	eax, eax
559
        jnz     .noinsert
559
	jnz	.noinsert
560
; 3c. Allocate the cache unless disabled by the driver. Abort if failed.
560
; 3c. Allocate the cache unless disabled by the driver. Abort if failed.
561
        call    disk_init_cache
561
	call	disk_init_cache
562
        test    al, al
562
	test	al, al
563
        jz      .noinsert
563
	jz	.noinsert
564
; 3d. Acquire the lifetime reference for the media object.
564
; 3d. Acquire the lifetime reference for the media object.
565
        inc     [esi+DISK.MediaRefCount]
565
	inc	[esi+DISK.MediaRefCount]
566
; 3e. Scan for partitions. Ignore result; the list of partitions is valid even
566
; 3e. Scan for partitions. Ignore result; the list of partitions is valid even
567
; on errors.
567
; on errors.
568
        call    disk_scan_partitions
568
	call	disk_scan_partitions
569
; 3f. Media is inserted and available for use.
569
; 3f. Media is inserted and available for use.
570
        inc     [esi+DISK.MediaInserted]
570
	inc	[esi+DISK.MediaInserted]
571
.noinsert:
571
.noinsert:
572
; 4. Return.
572
; 4. Return.
573
        pop     edi esi ebx             ; restore used registers to be stdcall
573
	pop	edi esi ebx		; restore used registers to be stdcall
Line 574... Line 574...
574
        ret     8                       ; purge 2 dword arguments to be stdcall
574
	ret	8			; purge 2 dword arguments to be stdcall
575
 
575
 
576
; This function is a thunk for all functions of a disk driver.
576
; This function is a thunk for all functions of a disk driver.
577
; It checks whether the referenced function is implemented in the driver.
577
; It checks whether the referenced function is implemented in the driver.
578
; If so, this function jumps to the function in the driver.
578
; If so, this function jumps to the function in the driver.
579
; Otherwise, it jumps to the default implementation.
579
; Otherwise, it jumps to the default implementation.
580
; al = offset of function in the DISKFUNC structure;
580
; al = offset of function in the DISKFUNC structure;
581
; esi = pointer to the DISK structure;
581
; esi = pointer to the DISK structure;
582
; stack is the same as for the corresponding function except that the
582
; stack is the same as for the corresponding function except that the
583
; first parameter (void* userdata) is prepended automatically.
583
; first parameter (void* userdata) is prepended automatically.
584
disk_call_driver:
584
disk_call_driver:
585
        movzx   eax, al ; eax = offset of function in the DISKFUNC structure
585
	movzx	eax, al ; eax = offset of function in the DISKFUNC structure
586
; 1. Prepend the first argument to the stack.
586
; 1. Prepend the first argument to the stack.
587
        pop     ecx     ; ecx = return address
587
	pop	ecx	; ecx = return address
588
        push    [esi+DISK.UserData]     ; add argument
588
	push	[esi+DISK.UserData]	; add argument
589
        push    ecx     ; save return address
589
	push	ecx	; save return address
590
; 2. Check that the required function is inside the table. If not, go to 5.
590
; 2. Check that the required function is inside the table. If not, go to 5.
591
        mov     ecx, [esi+DISK.Functions]
591
	mov	ecx, [esi+DISK.Functions]
592
        cmp     eax, [ecx+DISKFUNC.strucsize]
592
	cmp	eax, [ecx+DISKFUNC.strucsize]
593
        jae     .default
593
	jae	.default
594
; 3. Check that the required function is implemented. If not, go to 5.
594
; 3. Check that the required function is implemented. If not, go to 5.
595
        mov     ecx, [ecx+eax]
595
	mov	ecx, [ecx+eax]
596
        test    ecx, ecx
596
	test	ecx, ecx
597
        jz      .default
597
	jz	.default
598
; 4. Jump to the required function.
598
; 4. Jump to the required function.
599
        jmp     ecx
599
	jmp	ecx
600
.default:
600
.default:
Line 601... Line 601...
601
; 5. Driver does not implement the required function; use default implementation.
601
; 5. Driver does not implement the required function; use default implementation.
602
        jmp     dword [disk_default_callbacks+eax-4]
602
	jmp	dword [disk_default_callbacks+eax-4]
603
 
603
 
604
; The default implementation of DISKFUNC.querymedia.
604
; The default implementation of DISKFUNC.querymedia.
605
disk_default_querymedia:
605
disk_default_querymedia:
Line 606... Line 606...
606
        push    DISK_STATUS_INVALID_CALL
606
	push	DISK_STATUS_INVALID_CALL
607
        pop     eax
607
	pop	eax
608
        ret     8
608
	ret	8
609
 
609
 
610
; The default implementation of DISKFUNC.read and DISKFUNC.write.
610
; The default implementation of DISKFUNC.read and DISKFUNC.write.
611
disk_default_read:
611
disk_default_read:
Line 612... Line 612...
612
disk_default_write:
612
disk_default_write:
613
        push    DISK_STATUS_INVALID_CALL
613
	push	DISK_STATUS_INVALID_CALL
614
        pop     eax
614
	pop	eax
615
        ret     20
615
	ret	20
616
 
616
 
617
; The default implementation of DISKFUNC.close, DISKFUNC.closemedia and
617
; The default implementation of DISKFUNC.close, DISKFUNC.closemedia and
618
; DISKFUNC.flush.
618
; DISKFUNC.flush.
Line 619... Line 619...
619
disk_default_close:
619
disk_default_close:
620
disk_default_closemedia:
620
disk_default_closemedia:
621
disk_default_flush:
621
disk_default_flush:
622
        xor     eax, eax
622
	xor	eax, eax
Line 623... Line 623...
623
        ret     4
623
	ret	4
624
 
624
 
625
; The default implementation of DISKFUNC.adjust_cache_size.
625
; The default implementation of DISKFUNC.adjust_cache_size.
626
disk_default_adjust_cache_size:
626
disk_default_adjust_cache_size:
627
        mov     eax, [esp+4]
627
	mov	eax, [esp+4]
628
        ret     4
628
	ret	4
629
 
629
 
630
; This is an internal function called from 'disk_media_changed' when new media
630
; This is an internal function called from 'disk_media_changed' when a new media
631
; is detected. It creates the list of partitions for the media.
631
; is detected. It creates the list of partitions for the media.
632
; If media is not partitioned, then the list consists of one partition which
632
; If media is not partitioned, then the list consists of one partition which
633
; covers all the media.
633
; covers all the media.
634
; esi = pointer to the DISK structure.
634
; esi = pointer to the DISK structure.
635
disk_scan_partitions:
635
disk_scan_partitions:
636
; 1. Initialize .NumPartitions and .Partitions fields as zeros: empty list.
636
; 1. Initialize .NumPartitions and .Partitions fields as zeros: empty list.
637
        and     [esi+DISK.NumPartitions], 0
637
	and	[esi+DISK.NumPartitions], 0
638
        and     [esi+DISK.Partitions], 0
638
	and	[esi+DISK.Partitions], 0
639
; 2. Currently we can work only with 512-bytes sectors. Check this restriction.
639
; 2. Currently we can work only with 512-bytes sectors. Check this restriction.
640
; The only exception is 2048-bytes CD/DVD, but they are not supported yet by
640
; The only exception is 2048-bytes CD/DVD, but they are not supported yet by
641
; this code.
641
; this code.
642
        cmp     [esi+DISK.MediaInfo.SectorSize], 512
642
	cmp	[esi+DISK.MediaInfo.SectorSize], 512
643
        jz      .doscan
643
	jz	.doscan
644
        DEBUGF 1,'K : sector size is %d, only 512 is supported\n',[esi+DISK.MediaInfo.SectorSize]
644
	DEBUGF 1,'K : sector size is %d, only 512 is supported\n',[esi+DISK.MediaInfo.SectorSize]
645
        ret
645
	ret
646
.doscan:
646
.doscan:
647
; 3. Acquire the buffer for MBR and bootsector tests. See the comment before
647
; 3. Acquire the buffer for MBR and bootsector tests. See the comment before
648
; the 'partition_buffer_users' variable.
648
; the 'partition_buffer_users' variable.
649
        mov     ebx, mbr_buffer         ; assume the global buffer is free
649
	mov	ebx, mbr_buffer 	; assume the global buffer is free
650
lock    inc     [partition_buffer_users]
650
lock	inc	[partition_buffer_users]
651
        jz      .buffer_acquired        ; yes, it is free
651
	jz	.buffer_acquired	; yes, it is free
652
lock    dec     [partition_buffer_users]        ; no, we must allocate
652
lock	dec	[partition_buffer_users]	; no, we must allocate
653
        stdcall kernel_alloc, 512*3
653
	stdcall kernel_alloc, 512*3
654
        test    eax, eax
654
	test	eax, eax
655
        jz      .nothing
655
	jz	.nothing
656
        xchg    eax, ebx
656
	xchg	eax, ebx
657
.buffer_acquired:
657
.buffer_acquired:
658
; MBR/EBRs are organized in the chain. We use a loop over MBR/EBRs, but no
658
; MBR/EBRs are organized in the chain. We use a loop over MBR/EBRs, but no
659
; more than MAX_NUM_PARTITION times.
659
; more than MAX_NUM_PARTITION times.
660
; 4. Prepare things for the loop.
660
; 4. Prepare things for the loop.
661
; ebp will hold the sector number for current MBR/EBR.
661
; ebp will hold the sector number for current MBR/EBR.
662
; [esp] will hold the sector number for current extended partition, if there
662
; [esp] will hold the sector number for current extended partition, if there
663
; is one.
663
; is one.
664
; [esp+4] will hold the counter that prevents long loops.
664
; [esp+4] will hold the counter that prevents long loops.
665
        push    ebp             ; save ebp
665
	push	ebp		; save ebp
666
        push    MAX_NUM_PARTITIONS      ; the counter of max MBRs to process
666
	push	MAX_NUM_PARTITIONS	; the counter of max MBRs to process
667
        xor     ebp, ebp        ; start from sector zero
667
	xor	ebp, ebp	; start from sector zero
668
        push    ebp             ; no extended partition yet
668
	push	ebp		; no extended partition yet
669
.new_mbr:
669
.new_mbr:
670
; 5. Read the current sector.
670
; 5. Read the current sector.
671
; Note that 'read' callback operates with 64-bit sector numbers, so we must
671
; Note that 'read' callback operates with 64-bit sector numbers, so we must
672
; push additional zero as a high dword of sector number.
672
; push additional zero as a high dword of sector number.
673
        mov     al, DISKFUNC.read
673
	mov	al, DISKFUNC.read
674
        push    1
674
	push	1
675
        stdcall disk_call_driver, ebx, ebp, 0, esp
675
	stdcall disk_call_driver, ebx, ebp, 0, esp
676
        pop     ecx
676
	pop	ecx
677
; 6. If the read has failed, abort the loop.
677
; 6. If the read has failed, abort the loop.
678
        dec     ecx
678
	dec	ecx
679
        jnz     .mbr_failed
679
	jnz	.mbr_failed
680
; 7. Check the MBR/EBR signature. If it is wrong, abort the loop.
680
; 7. Check the MBR/EBR signature. If it is wrong, abort the loop.
681
; Soon we will access the partition table which starts at ebx+0x1BE,
681
; Soon we will access the partition table which starts at ebx+0x1BE,
682
; so we can fill its address right now. If we do it now, then the addressing
682
; so we can fill its address right now. If we do it now, then the addressing
683
; [ecx+0x40] is shorter than [ebx+0x1fe]: one-byte offset vs 4-bytes offset.
683
; [ecx+0x40] is shorter than [ebx+0x1fe]: one-byte offset vs 4-bytes offset.
684
        lea     ecx, [ebx+0x1be]        ; ecx -> partition table
684
	lea	ecx, [ebx+0x1be]	; ecx -> partition table
685
        cmp     word [ecx+0x40], 0xaa55
685
	cmp	word [ecx+0x40], 0xaa55
686
        jnz     .mbr_failed
686
	jnz	.mbr_failed
687
; 8. The MBR is treated differently from EBRs. For MBR we additionally need to
687
; 8. The MBR is treated differently from EBRs. For MBR we additionally need to
688
; execute step 9 and possibly step 10.
688
; execute step 9 and possibly step 10.
689
        test    ebp, ebp
689
	test	ebp, ebp
690
        jnz     .mbr
690
	jnz	.mbr
691
; Partition table can be present or not present. In the first case, we just
691
; The partition table can be present or not present. In the first case, we just
692
; read the MBR. In the second case, we just read the bootsector for some
692
; read the MBR. In the second case, we just read the bootsector for a
693
; filesystem.
693
; filesystem.
694
; We use the following algorithm to distinguish between these cases.
694
; The following algorithm is used to distinguish between these cases.
695
; A. If at least one entry of the partition table is invalid, this is
695
; A. If at least one entry of the partition table is invalid, this is
696
;    a bootsector. See the description of 'is_partition_table_entry' for
696
;    a bootsector. See the description of 'is_partition_table_entry' for
697
;    definition of validity.
697
;    definition of validity.
698
; B. If all entries are empty (filesystem type field is zero) and the first
698
; B. If all entries are empty (filesystem type field is zero) and the first
699
;    byte is jmp opcode (0EBh or 0E9h), this is a bootsector which happens to
699
;    byte is jmp opcode (0EBh or 0E9h), this is a bootsector which happens to
700
;    have zeros in the place of partition table.
700
;    have zeros in the place of partition table.
701
; C. Otherwise, this is a MBR.
701
; C. Otherwise, this is an MBR.
702
; 9. Test for MBR vs bootsector.
702
; 9. Test for MBR vs bootsector.
703
; 9a. Check entries. If any is invalid, go to 10 (rule A).
703
; 9a. Check entries. If any is invalid, go to 10 (rule A).
704
        call    is_partition_table_entry
704
	call	is_partition_table_entry
705
        jc      .notmbr
705
	jc	.notmbr
706
        add     ecx, 10h
706
	add	ecx, 10h
707
        call    is_partition_table_entry
707
	call	is_partition_table_entry
708
        jc      .notmbr
708
	jc	.notmbr
709
        add     ecx, 10h
709
	add	ecx, 10h
710
        call    is_partition_table_entry
710
	call	is_partition_table_entry
711
        jc      .notmbr
711
	jc	.notmbr
712
        add     ecx, 10h
712
	add	ecx, 10h
713
        call    is_partition_table_entry
713
	call	is_partition_table_entry
714
        jc      .notmbr
714
	jc	.notmbr
715
; 9b. Check types of the entries. If at least one is nonzero, go to 11 (rule C).
715
; 9b. Check types of the entries. If at least one is nonzero, go to 11 (rule C).
716
        mov     al, [ecx-30h+PARTITION_TABLE_ENTRY.Type]
716
	mov	al, [ecx-30h+PARTITION_TABLE_ENTRY.Type]
717
        or      al, [ecx-20h+PARTITION_TABLE_ENTRY.Type]
717
	or	al, [ecx-20h+PARTITION_TABLE_ENTRY.Type]
718
        or      al, [ecx-10h+PARTITION_TABLE_ENTRY.Type]
718
	or	al, [ecx-10h+PARTITION_TABLE_ENTRY.Type]
719
        or      al, [ecx+PARTITION_TABLE_ENTRY.Type]
719
	or	al, [ecx+PARTITION_TABLE_ENTRY.Type]
720
        jnz     .mbr
720
	jnz	.mbr
721
; 9c. Empty partition table or bootsector with many zeroes? (rule B)
721
; 9c. Empty partition table or bootsector with many zeroes? (rule B)
722
        cmp     byte [ebx], 0EBh
722
	cmp	byte [ebx], 0EBh
723
        jz      .notmbr
723
	jz	.notmbr
724
        cmp     byte [ebx], 0E9h
724
	cmp	byte [ebx], 0E9h
725
        jnz     .mbr
725
	jnz	.mbr
726
.notmbr:
726
.notmbr:
727
; 10. This is not MBR. The media is not partitioned. Create one partition
727
; 10. This is not an  MBR. The media is not partitioned. Create one partition
728
; which covers all the media and abort the loop.
728
; which covers all the media and abort the loop.
729
        stdcall disk_add_partition, 0, 0, \
729
	stdcall disk_add_partition, 0, 0, \
730
                dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4]
730
		dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4]
731
        jmp     .done
731
	jmp	.done
732
.mbr:
732
.mbr:
733
; 11. Process all entries of the new MBR/EBR
733
; 11. Process all entries of the new MBR/EBR
734
        lea     ecx, [ebx+0x1be]        ; ecx -> partition table
734
	lea	ecx, [ebx+0x1be]	; ecx -> partition table
735
        push    0       ; assume no extended partition
735
	push	0	; assume no extended partition
736
        call    process_partition_table_entry
736
	call	process_partition_table_entry
737
        add     ecx, 10h
737
	add	ecx, 10h
738
        call    process_partition_table_entry
738
	call	process_partition_table_entry
739
        add     ecx, 10h
739
	add	ecx, 10h
740
        call    process_partition_table_entry
740
	call	process_partition_table_entry
741
        add     ecx, 10h
741
	add	ecx, 10h
742
        call    process_partition_table_entry
742
	call	process_partition_table_entry
743
        pop     ebp
743
	pop	ebp
744
; 12. Test whether we found a new EBR and should continue the loop.
744
; 12. Test whether we found a new EBR and should continue the loop.
745
; 12a. If there was no next EBR, return.
745
; 12a. If there was no next EBR, return.
746
        test    ebp, ebp
746
	test	ebp, ebp
747
        jz      .done
747
	jz	.done
748
; Ok, we have EBR.
748
; Ok, we have EBR.
749
; 12b. EBRs addresses are relative to the start of extended partition.
749
; 12b. EBRs addresses are relative to the start of extended partition.
750
; For simplicity, just abort if an 32-bit overflow occurs; large disks
750
; For simplicity, just abort if an 32-bit overflow occurs; large disks
751
; are most likely partitioned with GPT, not MBR scheme, since the precise
751
; are most likely partitioned with GPT, not MBR scheme, since the precise
752
; calculation here would increase limit just twice at the price of big
752
; calculation here would increase limit just twice at the price of big
753
; compatibility problems.
753
; compatibility problems.
754
        pop     eax     ; load extended partition
754
	pop	eax	; load extended partition
755
        add     ebp, eax
755
	add	ebp, eax
756
        jc      .mbr_failed
756
	jc	.mbr_failed
757
; 12c. If extended partition has not yet started, start it.
757
; 12c. If extended partition has not yet started, start it.
758
        test    eax, eax
758
	test	eax, eax
759
        jnz     @f
759
	jnz	@f
760
        mov     eax, ebp
760
	mov	eax, ebp
761
@@:
761
@@:
762
; 12c. If the limit is not exceeded, continue the loop.
762
; 12c. If the limit is not exceeded, continue the loop.
763
        dec     dword [esp]
763
	dec	dword [esp]
764
        push    eax     ; store extended partition
764
	push	eax	; store extended partition
765
        jnz     .new_mbr
765
	jnz	.new_mbr
766
.mbr_failed:
766
.mbr_failed:
767
.done:
767
.done:
768
; 13. Cleanup after the loop.
768
; 13. Cleanup after the loop.
769
        pop     eax     ; not important anymore
769
	pop	eax	; not important anymore
770
        pop     eax     ; not important anymore
770
	pop	eax	; not important anymore
771
        pop     ebp     ; restore ebp
771
	pop	ebp	; restore ebp
772
; 14. Release the buffer.
772
; 14. Release the buffer.
773
; 14a. Test whether it is the global buffer or we have allocated it.
773
; 14a. Test whether it is the global buffer or we have allocated it.
774
        cmp     ebx, mbr_buffer
774
	cmp	ebx, mbr_buffer
775
        jz      .release_partition_buffer
775
	jz	.release_partition_buffer
776
; 14b. If we have allocated it, free it.
776
; 14b. If we have allocated it, free it.
777
        xchg    eax, ebx
777
	xchg	eax, ebx
778
        call    free
778
	call	free
Line 779... Line 779...
779
        jmp     .nothing
779
	jmp	.nothing
780
; 14c. Otherwise, release reference.
780
; 14c. Otherwise, release reference.
781
.release_partition_buffer:
781
.release_partition_buffer:
782
lock    dec     [partition_buffer_users]
782
lock	dec	[partition_buffer_users]
783
.nothing:
783
.nothing:
784
; 15. Return.
784
; 15. Return.
785
        ret
785
	ret
786
 
786
 
787
; This is an internal function called from disk_scan_partitions. It checks
787
; This is an internal function called from disk_scan_partitions. It checks
788
; whether the entry pointed to by ecx is a valid entry of partition table.
788
; whether the entry pointed to by ecx is a valid entry of partition table.
789
; The entry is valid if the first byte is 0 or 80h, the first sector plus the
789
; The entry is valid if the first byte is 0 or 80h, the first sector plus the
790
; length is less than twice the size of media. Multiplication by two is
790
; length is less than twice the size of media. Multiplication by two is
791
; required since the size mentioned in the partition table can be slightly
791
; required since the size mentioned in the partition table can be slightly
792
; greater than the real size.
792
; greater than the real size.
793
is_partition_table_entry:
793
is_partition_table_entry:
794
; 1. Check .Bootable field.
794
; 1. Check .Bootable field.
795
        mov     al, [ecx+PARTITION_TABLE_ENTRY.Bootable]
795
	mov	al, [ecx+PARTITION_TABLE_ENTRY.Bootable]
796
        and     al, 7Fh
796
	and	al, 7Fh
797
        jnz     .invalid
797
	jnz	.invalid
798
; 3. Calculate first sector + length. Note that .FirstAbsSector is relative
798
; 3. Calculate first sector + length. Note that .FirstAbsSector is relative
799
; to the MBR/EBR, so the real sum is ebp + .FirstAbsSector + .Length.
799
; to the MBR/EBR, so the real sum is ebp + .FirstAbsSector + .Length.
800
        mov     eax, ebp
800
	mov	eax, ebp
801
        xor     edx, edx
801
	xor	edx, edx
802
        add     eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
802
	add	eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
803
        adc     edx, 0
803
	adc	edx, 0
804
        add     eax, [ecx+PARTITION_TABLE_ENTRY.Length]
804
	add	eax, [ecx+PARTITION_TABLE_ENTRY.Length]
805
        adc     edx, 0
805
	adc	edx, 0
806
; 4. Divide by two.
806
; 4. Divide by two.
807
        shr     edx, 1
807
	shr	edx, 1
808
        rcr     eax, 1
808
	rcr	eax, 1
809
; 5. Compare with capacity. If the subtraction (edx:eax) - .Capacity does not
809
; 5. Compare with capacity. If the subtraction (edx:eax) - .Capacity does not
810
; overflow, this is bad.
810
; overflow, this is bad.
811
        sub     eax, dword [esi+DISK.MediaInfo.Capacity]
811
	sub	eax, dword [esi+DISK.MediaInfo.Capacity]
812
        sbb     edx, dword [esi+DISK.MediaInfo.Capacity+4]
812
	sbb	edx, dword [esi+DISK.MediaInfo.Capacity+4]
813
        jnc     .invalid
813
	jnc	.invalid
Line 814... Line 814...
814
.valid:
814
.valid:
815
; 5. Return success: CF is cleared.
815
; 5. Return success: CF is cleared.
816
        clc
816
	clc
817
        ret
817
	ret
Line 829... Line 829...
829
; * Otherwise, add the partition to the list of partitions for this disk.
829
; * Otherwise, add the partition to the list of partitions for this disk.
830
;   We don't use the type from the entry to identify the file system;
830
;   We don't use the type from the entry to identify the file system;
831
;   fs-specific checks do this more reliably.
831
;   fs-specific checks do this more reliably.
832
process_partition_table_entry:
832
process_partition_table_entry:
833
; 1. Check for valid entry. If invalid, return (go to 5).
833
; 1. Check for valid entry. If invalid, return (go to 5).
834
        call    is_partition_table_entry
834
	call	is_partition_table_entry
835
        jc      .nothing
835
	jc	.nothing
836
; 2. Check for empty entry. If invalid, return (go to 5).
836
; 2. Check for empty entry. If invalid, return (go to 5).
837
        mov     al, [ecx+PARTITION_TABLE_ENTRY.Type]
837
	mov	al, [ecx+PARTITION_TABLE_ENTRY.Type]
838
        test    al, al
838
	test	al, al
839
        jz      .nothing
839
	jz	.nothing
840
; 3. Check for extended partition. If extended, go to 6.
840
; 3. Check for extended partition. If extended, go to 6.
841
irp type,\
841
irp type,\
842
    0x05,\                 ; DOS: extended partition
842
    0x05,\		   ; DOS: extended partition
843
    0x0f,\                 ; WIN95: extended partition, LBA-mapped
843
    0x0f,\		   ; WIN95: extended partition, LBA-mapped
844
    0xc5,\                 ; DRDOS/secured: extended partition
844
    0xc5,\		   ; DRDOS/secured: extended partition
845
    0xd5                   ; Old Multiuser DOS secured: extended partition
845
    0xd5		   ; Old Multiuser DOS secured: extended partition
846
{
846
{
847
        cmp     al, type
847
	cmp	al, type
848
        jz      .extended
848
	jz	.extended
849
}
849
}
850
; 4. If we are here, that is a normal partition. Add it to the list.
850
; 4. If we are here, that is a normal partition. Add it to the list.
851
; Note that the first sector is relative to MBR/EBR.
851
; Note that the first sector is relative to MBR/EBR.
852
        mov     eax, ebp
852
	mov	eax, ebp
853
        xor     edx, edx
853
	xor	edx, edx
854
        add     eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
854
	add	eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
855
        adc     edx, 0
855
	adc	edx, 0
856
        push    ecx
856
	push	ecx
857
        stdcall disk_add_partition, eax, edx, \
857
	stdcall disk_add_partition, eax, edx, \
858
                [ecx+PARTITION_TABLE_ENTRY.Length], 0
858
		[ecx+PARTITION_TABLE_ENTRY.Length], 0
859
        pop     ecx
859
	pop	ecx
860
.nothing:
860
.nothing:
861
; 5. Return.
861
; 5. Return.
862
        ret
862
	ret
863
.extended:
863
.extended:
864
; 6. If we are here, that is an extended partition. Store the address.
864
; 6. If we are here, that is an extended partition. Store the address.
865
        mov     eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
865
	mov	eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector]
866
        mov     [esp+4], eax
866
	mov	[esp+4], eax
867
        ret
867
	ret
Line 868... Line 868...
868
 
868
 
869
; This is an internal function called from disk_scan_partitions and
869
; This is an internal function called from disk_scan_partitions and
870
; process_partition_table_entry. It adds one partition to the list of
870
; process_partition_table_entry. It adds one partition to the list of
871
; partitions for the media.
871
; partitions for the media.
872
proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword
872
proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword
873
; 1. Check that this partition will not exceed the limit on total number.
873
; 1. Check that this partition will not exceed the limit on total number.
874
        cmp     [esi+DISK.NumPartitions], MAX_NUM_PARTITIONS
874
	cmp	[esi+DISK.NumPartitions], MAX_NUM_PARTITIONS
875
        jae     .nothing
875
	jae	.nothing
876
; 2. Check that this partition does not overlap with any already registered
876
; 2. Check that this partition does not overlap with any already registered
877
; partition. Since any file system assumes that the disk data will not change
877
; partition. Since any file system assumes that the disk data will not change
878
; outside of its control, such overlap could be destructive.
878
; outside of its control, such overlap could be destructive.
879
; Since the number of partitions is usually very small and is guaranteed not
879
; Since the number of partitions is usually very small and is guaranteed not
880
; to be large, the simple linear search is sufficient.
880
; to be large, the simple linear search is sufficient.
881
; 2a. Prepare the loop: edi will point to the current item of .Partitions
881
; 2a. Prepare the loop: edi will point to the current item of .Partitions
882
; array, ecx will be the current item, ebx will hold number of items left.
882
; array, ecx will be the current item, ebx will hold number of items left.
883
        mov     edi, [esi+DISK.Partitions]
883
	mov	edi, [esi+DISK.Partitions]
884
        mov     ebx, [esi+DISK.NumPartitions]
884
	mov	ebx, [esi+DISK.NumPartitions]
885
        test    ebx, ebx
885
	test	ebx, ebx
886
        jz      .partitionok
886
	jz	.partitionok
887
.scan_existing:
887
.scan_existing:
888
; 2b. Get the next partition.
888
; 2b. Get the next partition.
889
        mov     ecx, [edi]
889
	mov	ecx, [edi]
890
        add     edi, 4
890
	add	edi, 4
891
; The range [.FirstSector, .FirstSector+.Length) must be either entirely to
891
; The range [.FirstSector, .FirstSector+.Length) must be either entirely to
892
; the left of [start, start+length) or entirely to the right.
892
; the left of [start, start+length) or entirely to the right.
893
; 2c. Subtract .FirstSector - start. The possible overflow distinguish between
893
; 2c. Subtract .FirstSector - start. The possible overflow distinguish between
894
; cases "to the left" (2e) and "to the right" (2d).
894
; cases "to the left" (2e) and "to the right" (2d).
895
        mov     eax, dword [ecx+PARTITION.FirstSector]
895
	mov	eax, dword [ecx+PARTITION.FirstSector]
896
        mov     edx, dword [ecx+PARTITION.FirstSector+4]
896
	mov	edx, dword [ecx+PARTITION.FirstSector+4]
897
        sub     eax, dword [start]
897
	sub	eax, dword [start]
898
        sbb     edx, dword [start+4]
898
	sbb	edx, dword [start+4]
899
        jb      .less
899
	jb	.less
900
; 2d. .FirstSector is greater than or equal to start. Check that .FirstSector
900
; 2d. .FirstSector is greater than or equal to start. Check that .FirstSector
901
; is greater than or equal to start+length; the subtraction
901
; is greater than or equal to start+length; the subtraction
902
; (.FirstSector-start) - length must not cause overflow. Go to 2g if life is
902
; (.FirstSector-start) - length must not cause overflow. Go to 2g if life is
903
; good or to 2f in the other case.
903
; good or to 2f in the other case.
904
        sub     eax, dword [length]
904
	sub	eax, dword [length]
905
        sbb     edx, dword [length+4]
905
	sbb	edx, dword [length+4]
906
        jb      .overlap
906
	jb	.overlap
907
        jmp     .next_existing
907
	jmp	.next_existing
908
.less:
908
.less:
909
; 2e. .FirstSector is less than start. Check that .FirstSector+.Length is less
909
; 2e. .FirstSector is less than start. Check that .FirstSector+.Length is less
910
; than or equal to start. If the addition (.FirstSector-start) + .Length does
910
; than or equal to start. If the addition (.FirstSector-start) + .Length does
911
; not cause overflow, then .FirstSector + .Length is strictly less than start;
911
; not cause overflow, then .FirstSector + .Length is strictly less than start;
912
; since the equality is also valid, use decrement preliminarily. Go to 2g or
912
; since the equality is also valid, use decrement preliminarily. Go to 2g or
913
; 2f depending on the overflow.
913
; 2f depending on the overflow.
914
        sub     eax, 1
914
	sub	eax, 1
915
        sbb     edx, 0
915
	sbb	edx, 0
916
        add     eax, dword [ecx+PARTITION.Length]
916
	add	eax, dword [ecx+PARTITION.Length]
917
        adc     edx, dword [ecx+PARTITION.Length+4]
917
	adc	edx, dword [ecx+PARTITION.Length+4]
918
        jnc     .next_existing
918
	jnc	.next_existing
919
.overlap:
919
.overlap:
920
; 2f. The partition overlaps with previously registered partition. Say warning
920
; 2f. The partition overlaps with previously registered partition. Say warning
921
; and return with nothing done.
921
; and return with nothing done.
922
        dbgstr 'two partitions overlap, ignoring the last one'
922
	dbgstr 'two partitions overlap, ignoring the last one'
923
        jmp     .nothing
923
	jmp	.nothing
924
.next_existing:
924
.next_existing:
925
; 2g. The partition does not overlap with the current partition. Continue the
925
; 2g. The partition does not overlap with the current partition. Continue the
926
; loop.
926
; loop.
927
        dec     ebx
927
	dec	ebx
928
        jnz     .scan_existing
928
	jnz	.scan_existing
929
.partitionok:
929
.partitionok:
930
; 3. The partition has passed tests. Reallocate the partitions array for a new
930
; 3. The partition has passed tests. Reallocate the partitions array for a new
931
; entry.
931
; entry.
932
; 3a. Call the allocator.
932
; 3a. Call the allocator.
933
        mov     eax, [esi+DISK.NumPartitions]
933
	mov	eax, [esi+DISK.NumPartitions]
934
        inc     eax     ; one more entry
934
	inc	eax	; one more entry
935
        shl     eax, 2  ; each entry is dword
935
	shl	eax, 2	; each entry is dword
936
        call    malloc
936
	call	malloc
937
; 3b. Test the result. If failed, return with nothing done.
937
; 3b. Test the result. If failed, return with nothing done.
938
        test    eax, eax
938
	test	eax, eax
939
        jz      .nothing
939
	jz	.nothing
940
; 3c. Copy the old array to the new array.
940
; 3c. Copy the old array to the new array.
941
        mov     edi, eax
941
	mov	edi, eax
942
        push    esi
942
	push	esi
943
        mov     ecx, [esi+DISK.NumPartitions]
943
	mov	ecx, [esi+DISK.NumPartitions]
944
        mov     esi, [esi+DISK.Partitions]
944
	mov	esi, [esi+DISK.Partitions]
945
        rep     movsd
945
	rep	movsd
946
        pop     esi
946
	pop	esi
947
; 3d. Set the field in the DISK structure to the new array.
947
; 3d. Set the field in the DISK structure to the new array.
948
        xchg    [esi+DISK.Partitions], eax
948
	xchg	[esi+DISK.Partitions], eax
949
; 3e. Free the old array.
949
; 3e. Free the old array.
950
        call    free
950
	call	free
951
; 4. Recognize the file system.
951
; 4. Recognize the file system.
952
; 4a. Call the filesystem recognizer. It will allocate the PARTITION structure
952
; 4a. Call the filesystem recognizer. It will allocate the PARTITION structure
953
; with possible filesystem-specific fields.
953
; with possible filesystem-specific fields.
954
        call    disk_detect_partition
954
	call	disk_detect_partition
955
; 4b. Check return value. If zero, return with list not changed; so far only
955
; 4b. Check return value. If zero, return with list not changed; so far only
956
; the array was reallocated, this is ok for other code.
956
; the array was reallocated, this is ok for other code.
957
        test    eax, eax
957
	test	eax, eax
958
        jz      .nothing
958
	jz	.nothing
959
; 5. Insert the new partition to the list.
959
; 5. Insert the new partition to the list.
960
        stosd
960
	stosd
961
        inc     [esi+DISK.NumPartitions]
961
	inc	[esi+DISK.NumPartitions]
962
; 6. Return.
962
; 6. Return.
963
.nothing:
963
.nothing:
964
        ret
964
	ret
Line 965... Line 965...
965
endp
965
endp
966
 
966
 
967
; This is an internal function called from disk_add_partition.
967
; This is an internal function called from disk_add_partition.
968
; It tries to recognize the file system on the partition and allocates the
968
; It tries to recognize the file system on the partition and allocates the
969
; corresponding PARTITION structure with filesystem-specific fields.
969
; corresponding PARTITION structure with filesystem-specific fields.
970
disk_detect_partition:
970
disk_detect_partition:
971
; This function inherits the stack frame from disk_add_partition. In stdcall
971
; This function inherits the stack frame from disk_add_partition. In stdcall
972
; with ebp-based frame arguments start from ebp+8, since [ebp]=saved ebp
972
; with ebp-based frame arguments start from ebp+8, since [ebp]=saved ebp
973
; and [ebp+4]=return address.
973
; and [ebp+4]=return address.
974
virtual at ebp+8
974
virtual at ebp+8
975
.start  dq      ?
975
.start	dq	?
976
.length dq      ?
976
.length dq	?
977
end virtual
977
end virtual
978
; Currently no file systems are supported, so just allocate the PARTITION
978
; Currently no file systems are supported, so just allocate the PARTITION
979
; structure without extra fields.
979
; structure without extra fields.
980
; 1. Allocate and check result.
980
; 1. Allocate and check result.
981
        push    sizeof.PARTITION
981
	push	sizeof.PARTITION
982
        pop     eax
982
	pop	eax
983
        call    malloc
983
	call	malloc
984
        test    eax, eax
984
	test	eax, eax
985
        jz      .nothing
985
	jz	.nothing
986
; 2. Fill the common fields: copy .start and .length.
986
; 2. Fill the common fields: copy .start and .length.
987
        mov     edx, dword [.start]
987
	mov	edx, dword [.start]
988
        mov     dword [eax+PARTITION.FirstSector], edx
988
	mov	dword [eax+PARTITION.FirstSector], edx
989
        mov     edx, dword [.start+4]
989
	mov	edx, dword [.start+4]
990
        mov     dword [eax+PARTITION.FirstSector+4], edx
990
	mov	dword [eax+PARTITION.FirstSector+4], edx
991
        mov     edx, dword [.length]
991
	mov	edx, dword [.length]
992
        mov     dword [eax+PARTITION.Length], edx
992
	mov	dword [eax+PARTITION.Length], edx
993
        mov     edx, dword [.length+4]
993
	mov	edx, dword [.length+4]
994
        mov     dword [eax+PARTITION.Length+4], edx
994
	mov	dword [eax+PARTITION.Length+4], edx
995
.nothing:
995
.nothing:
Line 996... Line 996...
996
; 3. Return with eax = pointer to PARTITION or NULL.
996
; 3. Return with eax = pointer to PARTITION or NULL.
997
        ret
997
	ret
998
 
998
 
999
; This function is called from file_system_lfn.
999
; This function is called from file_system_lfn.
1000
; This handler gets the control each time when fn 70 is called
1000
; This handler gets the control each time when fn 70 is called
1001
; with unknown item of root subdirectory.
1001
; with unknown item of root subdirectory.
1002
; in: esi -> name
1002
; in: esi -> name
1003
;     ebp = 0 or rest of name relative to esi
1003
;     ebp = 0 or rest of name relative to esi
1004
; out: if the handler processes path, it must not return in file_system_lfn,
1004
; out: if the handler processes path, it must not return in file_system_lfn,
1005
;      but instead pop return address and return directly to the caller
1005
;      but instead pop return address and return directly to the caller
1006
;      otherwise simply return
1006
;      otherwise simply return
1007
dyndisk_handler:
1007
dyndisk_handler:
1008
        push    ebx edi         ; save registers used in file_system_lfn
1008
	push	ebx edi 	; save registers used in file_system_lfn
1009
; 1. Acquire the mutex.
1009
; 1. Acquire the mutex.
1010
        mov     ecx, disk_list_mutex
1010
	mov	ecx, disk_list_mutex
1011
        call    mutex_lock
1011
	call	mutex_lock
1012
; 2. Loop over the list of DISK structures.
1012
; 2. Loop over the list of DISK structures.
1013
; 2a. Initialize.
1013
; 2a. Initialize.
1014
        mov     ebx, disk_list
1014
	mov	ebx, disk_list
1015
.scan:
1015
.scan:
1016
; 2b. Get the next item.
1016
; 2b. Get the next item.
1017
        mov     ebx, [ebx+DISK.Next]
1017
	mov	ebx, [ebx+DISK.Next]
1018
; 2c. Check whether the list is done. If so, go to 3.
1018
; 2c. Check whether the list is done. If so, go to 3.
1019
        cmp     ebx, disk_list
1019
	cmp	ebx, disk_list
1020
        jz      .notfound
1020
	jz	.notfound
1021
; 2d. Compare names. If names match, go to 5.
1021
; 2d. Compare names. If names match, go to 5.
1022
        mov     edi, [ebx+DISK.Name]
1022
	mov	edi, [ebx+DISK.Name]
1023
        push    esi
1023
	push	esi
1024
@@:
1024
@@:
1025
; esi points to the name from fs operation; it is terminated by zero or slash.
1025
; esi points to the name from fs operation; it is terminated by zero or slash.
1026
        lodsb
1026
	lodsb
1027
        test    al, al
1027
	test	al, al
1028
        jz      .eoin_dec
1028
	jz	.eoin_dec
1029
        cmp     al, '/'
1029
	cmp	al, '/'
1030
        jz      .eoin
1030
	jz	.eoin
1031
; edi points to the disk name.
1031
; edi points to the disk name.
1032
        inc     edi
1032
	inc	edi
1033
; edi points to lowercase name, this is a requirement for the driver.
1033
; edi points to lowercase name, this is a requirement for the driver.
1034
; Characters at esi can have any register. Lowercase the current character.
1034
; Characters at esi can have any register. Lowercase the current character.
1035
; This lowercasing works for latin letters and digits; since the disk name
1035
; This lowercasing works for latin letters and digits; since the disk name
1036
; should not contain other symbols, this is ok.
1036
; should not contain other symbols, this is ok.
1037
        or      al, 20h
1037
	or	al, 20h
1038
        cmp     al, [edi-1]
1038
	cmp	al, [edi-1]
1039
        jz      @b
1039
	jz	@b
1040
.wrongname:
1040
.wrongname:
1041
; 2f. Names don't match. Continue the loop.
1041
; 2f. Names don't match. Continue the loop.
1042
        pop     esi
1042
	pop	esi
1043
        jmp     .scan
1043
	jmp	.scan
1044
.notfound:
1044
.notfound:
1045
; The loop is done and no name matches.
1045
; The loop is done and no name matches.
1046
; 3. Release the mutex.
1046
; 3. Release the mutex.
1047
        call mutex_unlock
1047
	call mutex_unlock
1048
; 4. Return normally.
1048
; 4. Return normally.
1049
        pop     edi ebx         ; restore registers used in file_system_lfn
1049
	pop	edi ebx 	; restore registers used in file_system_lfn
1050
        ret
1050
	ret
1051
; part of 2d: the name matches partially, but we must check that this is full
1051
; part of 2d: the name matches partially, but we must check that this is full
1052
; equality.
1052
; equality.
1053
.eoin_dec:
1053
.eoin_dec:
1054
        dec     esi
1054
	dec	esi
1055
.eoin:
1055
.eoin:
1056
        cmp     byte [edi], 0
1056
	cmp	byte [edi], 0
1057
        jnz     .wrongname
1057
	jnz	.wrongname
1058
; We found the addressed DISK structure.
1058
; We found the addressed DISK structure.
1059
; 5. Reference the disk.
1059
; 5. Reference the disk.
1060
lock    inc     [ebx+DISK.RefCount]
1060
lock	inc	[ebx+DISK.RefCount]
1061
; 6. Now we are sure that the DISK structure is not going to die at least
1061
; 6. Now we are sure that the DISK structure is not going to die at least
1062
; while we are working with it, so release the global mutex.
1062
; while we are working with it, so release the global mutex.
1063
        call    mutex_unlock
1063
	call	mutex_unlock
1064
; 7. Acquire the mutex for media object.
1064
; 7. Acquire the mutex for media object.
1065
        pop     edi             ; restore edi
1065
	pop	edi		; restore edi
1066
        lea     ecx, [ebx+DISK.MediaLock]
1066
	lea	ecx, [ebx+DISK.MediaLock]
1067
        call    mutex_lock
1067
	call	mutex_lock
1068
; 8. Get the media object. If it is not NULL, reference it.
1068
; 8. Get the media object. If it is not NULL, reference it.
1069
        xor     edx, edx
1069
	xor	edx, edx
1070
        cmp     [ebx+DISK.MediaInserted], dl
1070
	cmp	[ebx+DISK.MediaInserted], dl
1071
        jz      @f
1071
	jz	@f
1072
        mov     edx, ebx
1072
	mov	edx, ebx
1073
        inc     [ebx+DISK.MediaRefCount]
1073
	inc	[ebx+DISK.MediaRefCount]
1074
@@:
1074
@@:
1075
; 9. Now we are sure that the media object, if it exists, is not going to die
1075
; 9. Now we are sure that the media object, if it exists, is not going to die
1076
; at least while we are working with it, so release the mutex for media object.
1076
; at least while we are working with it, so release the mutex for media object.
1077
        call    mutex_unlock
1077
	call	mutex_unlock
1078
        mov     ecx, ebx
1078
	mov	ecx, ebx
1079
        pop     ebx eax         ; restore ebx, pop return address
1079
	pop	ebx eax 	; restore ebx, pop return address
1080
; 10. Check whether the fs operation wants to enumerate partitions (go to 11)
1080
; 10. Check whether the fs operation wants to enumerate partitions (go to 11)
1081
; or work with some concrete partition (go to 12).
1081
; or work with some concrete partition (go to 12).
1082
        cmp     byte [esi], 0
1082
	cmp	byte [esi], 0
1083
        jnz     .haspartition
1083
	jnz	.haspartition
1084
; 11. The fs operation wants to enumerate partitions.
1084
; 11. The fs operation wants to enumerate partitions.
1085
; 11a. Only "list directory" operation is applicable to / path. Check
1085
; 11a. Only "list directory" operation is applicable to / path. Check
1086
; the operation code. If wrong, go to 13.
1086
; the operation code. If wrong, go to 13.
1087
        cmp     dword [ebx], 1
1087
	cmp	dword [ebx], 1
1088
        jnz     .access_denied
1088
	jnz	.access_denied
1089
; 11b. If the media is inserted, use 'fs_dyndisk_next' as an enumeration
1089
; 11b. If the media is inserted, use 'fs_dyndisk_next' as an enumeration
1090
; procedure. Otherwise, use 'fs_dyndisk_next_nomedia'.
1090
; procedure. Otherwise, use 'fs_dyndisk_next_nomedia'.
1091
        mov     esi, fs_dyndisk_next_nomedia
1091
	mov	esi, fs_dyndisk_next_nomedia
1092
        test    edx, edx
1092
	test	edx, edx
1093
        jz      @f
1093
	jz	@f
1094
        mov     esi, fs_dyndisk_next
1094
	mov	esi, fs_dyndisk_next
1095
@@:
1095
@@:
1096
; 11c. Let the procedure from fs_lfn.inc do the job.
1096
; 11c. Let the procedure from fs_lfn.inc do the job.
1097
        jmp     file_system_lfn.maindir_noesi
1097
	jmp	file_system_lfn.maindir_noesi
1098
.haspartition:
1098
.haspartition:
1099
; 12. The fs operation has specified some partition.
1099
; 12. The fs operation has specified some partition.
1100
; 12a. Store parameters for callback functions.
1100
; 12a. Store parameters for callback functions.
1101
        push    edx
1101
	push	edx
1102
        push    ecx
1102
	push	ecx
1103
; 12b. Store callback functions.
1103
; 12b. Store callback functions.
1104
        push    dyndisk_cleanup
1104
	push	dyndisk_cleanup
1105
        push    fs_dyndisk
1105
	push	fs_dyndisk
1106
        mov     edi, esp
1106
	mov	edi, esp
1107
; 12c. Let the procedure from fs_lfn.inc do the job.
1107
; 12c. Let the procedure from fs_lfn.inc do the job.
1108
        jmp     file_system_lfn.found2
1108
	jmp	file_system_lfn.found2
1109
.access_denied:
1109
.access_denied:
1110
; 13. Fail the operation with the appropriate code.
1110
; 13. Fail the operation with the appropriate code.
1111
        mov     dword [esp+32], ERROR_ACCESS_DENIED
1111
	mov	dword [esp+32], ERROR_ACCESS_DENIED
1112
.cleanup:
1112
.cleanup:
1113
; 14. Cleanup.
1113
; 14. Cleanup.
1114
        mov     esi, ecx        ; disk*dereference assume that esi points to DISK
1114
	mov	esi, ecx	; disk*dereference assume that esi points to DISK
1115
.cleanup_esi:
1115
.cleanup_esi:
1116
        test    edx, edx        ; if there are no media, we didn't reference it
1116
	test	edx, edx	; if there are no media, we didn't reference it
1117
        jz      @f
1117
	jz	@f
1118
        call    disk_media_dereference
1118
	call	disk_media_dereference
1119
@@:
1119
@@:
Line 1120... Line 1120...
1120
        call    disk_dereference
1120
	call	disk_dereference
1121
; 15. Return.
1121
; 15. Return.
1122
        ret
1122
	ret
1123
 
1123
 
1124
; This is a callback for cleaning up things called from file_system_lfn.found2.
1124
; This is a callback for cleaning up things called from file_system_lfn.found2.
Line 1125... Line 1125...
1125
dyndisk_cleanup:
1125
dyndisk_cleanup:
1126
        mov     esi, [edi+8]
1126
	mov	esi, [edi+8]
1127
        mov     edx, [edi+12]
1127
	mov	edx, [edi+12]
1128
        jmp     dyndisk_handler.cleanup_esi
1128
	jmp	dyndisk_handler.cleanup_esi
1129
 
1129
 
1130
; This is a callback for enumerating partitions called from
1130
; This is a callback for enumerating partitions called from
1131
; file_system_lfn.maindir in the case of inserted media.
1131
; file_system_lfn.maindir in the case of inserted media.
1132
; It just increments eax until DISK.NumPartitions reached and then
1132
; It just increments eax until DISK.NumPartitions reached and then
1133
; cleans up.
1133
; cleans up.
1134
fs_dyndisk_next:
1134
fs_dyndisk_next:
1135
        cmp     eax, [ecx+DISK.NumPartitions]
1135
	cmp	eax, [ecx+DISK.NumPartitions]
1136
        jae     .nomore
1136
	jae	.nomore
1137
        inc     eax
1137
	inc	eax
1138
        clc
1138
	clc
1139
        ret
1139
	ret
1140
.nomore:
1140
.nomore:
1141
        pusha
1141
	pusha
1142
        mov     esi, ecx
1142
	mov	esi, ecx
Line 1143... Line 1143...
1143
        call    disk_media_dereference
1143
	call	disk_media_dereference
1144
        call    disk_dereference
1144
	call	disk_dereference
1145
        popa
1145
	popa
1146
        stc
1146
	stc
1147
        ret
1147
	ret
1148
 
1148
 
1149
; This is a callback for enumerating partitions called from
1149
; This is a callback for enumerating partitions called from
1150
; file_system_lfn.maindir in the case of missing media.
1150
; file_system_lfn.maindir in the case of missing media.
1151
; In this case we create one pseudo-partition.
1151
; In this case we create one pseudo-partition.
1152
fs_dyndisk_next_nomedia:
1152
fs_dyndisk_next_nomedia:
1153
        cmp     eax, 1
1153
	cmp	eax, 1
1154
        jae     .nomore
1154
	jae	.nomore
1155
        inc     eax
1155
	inc	eax
1156
        clc
1156
	clc
1157
        ret
1157
	ret
1158
.nomore:
1158
.nomore:
Line 1159... Line 1159...
1159
        pusha
1159
	pusha
1160
        mov     esi, ecx
1160
	mov	esi, ecx
1161
        call    disk_dereference
1161
	call	disk_dereference
1162
        popa
1162
	popa
1163
        stc
1163
	stc
1164
        ret
1164
	ret
1165
 
1165
 
1166
; This is a callback for doing real work with selected partition.
1166
; This is a callback for doing real work with selected partition.
1167
; Currently this is just placeholder, since no file systems are supported.
1167
; Currently this is just placeholder, since no file systems are supported.
1168
; edi = esp -> {dd fs_dyndisk, dd dyndisk_cleanup, dd pointer to DISK, dd media object}
1168
; edi = esp -> {dd fs_dyndisk, dd dyndisk_cleanup, dd pointer to DISK, dd media object}
1169
; ecx = partition number, esi+ebp = ASCIIZ name
1169
; ecx = partition number, esi+ebp = ASCIIZ name
1170
fs_dyndisk:
1170
fs_dyndisk:
1171
        dec     ecx     ; convert to zero-based partition index
1171
	dec	ecx	; convert to zero-based partition index
1172
        pop     edx edx edx eax ; edx = pointer to DISK, eax = NULL or edx
1172
	pop	edx edx edx eax ; edx = pointer to DISK, eax = NULL or edx
1173
        test    eax, eax
1173
	test	eax, eax
1174
        jz      .nomedia
1174
	jz	.nomedia
1175
.main:
1175
.main:
1176
        cmp     ecx, [edx+DISK.NumPartitions]
1176
	cmp	ecx, [edx+DISK.NumPartitions]
1177
        jae     .notfound
1177
	jae	.notfound
1178
        mov     dword [esp+32], ERROR_UNKNOWN_FS
1178
	mov	dword [esp+32], ERROR_UNKNOWN_FS
1179
.cleanup:
1179
.cleanup:
1180
        mov     esi, edx
1180
	mov	esi, edx
1181
        call    disk_media_dereference
1181
	call	disk_media_dereference
1182
        call    disk_dereference
1182
	call	disk_dereference
1183
        ret
1183
	ret
1184
.notfound:
1184
.notfound:
1185
        mov     dword [esp+32], ERROR_FILE_NOT_FOUND
1185
	mov	dword [esp+32], ERROR_FILE_NOT_FOUND
1186
        jmp     .cleanup
1186
	jmp	.cleanup
1187
.nomedia:
1187
.nomedia:
1188
        test    ecx, ecx
1188
	test	ecx, ecx
1189
        jnz     .notfound
1189
	jnz	.notfound
1190
        test    byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION
1190
	test	byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION
1191
        jz      .deverror
1191
	jz	.deverror
1192
; if the driver does not support insert notifications and we are the only fs
1192
; if the driver does not support insert notifications and we are the only fs
1193
; operation with this disk, issue the fake insert notification; if media is
1193
; operation with this disk, issue the fake insert notification; if media is
1194
; still not inserted, 'disk_media_changed' will detect this and do nothing
1194
; still not inserted, 'disk_media_changed' will detect this and do nothing
1195
;;;        push    ebx
1195
;;;        push    ebx
1196
        lea     ecx, [edx+DISK.MediaLock]
1196
	lea	ecx, [edx+DISK.MediaLock]
1197
        call    mutex_lock
1197
	call	mutex_lock
1198
        cmp     [edx+DISK.MediaRefCount], 1
1198
	cmp	[edx+DISK.MediaRefCount], 1
1199
        jnz     .noluck
1199
	jnz	.noluck
1200
        call    mutex_unlock
1200
	call	mutex_unlock
1201
        push    edx
1201
	push	edx
1202
        stdcall disk_media_changed, edx, 1
1202
	stdcall disk_media_changed, edx, 1
1203
        pop     edx
1203
	pop	edx
1204
        lea     ecx, [edx+DISK.MediaLock]
1204
	lea	ecx, [edx+DISK.MediaLock]
1205
        call    mutex_lock
1205
	call	mutex_lock
1206
        cmp     [edx+DISK.MediaInserted], 0
1206
	cmp	[edx+DISK.MediaInserted], 0
1207
        jz      .noluck
1207
	jz	.noluck
1208
lock    inc     [edx+DISK.MediaRefCount]
1208
lock	inc	[edx+DISK.MediaRefCount]
1209
        call    mutex_unlock
1209
	call	mutex_unlock
1210
        xor     ecx, ecx
1210
	xor	ecx, ecx
1211
        jmp     .main
1211
	jmp	.main
Line 1212... Line 1212...
1212
.noluck:
1212
.noluck:
1213
        call    mutex_unlock
1213
	call	mutex_unlock
1214
.deverror:
1214
.deverror:
1215
        mov     dword [esp+32], ERROR_DEVICE
1215
	mov	dword [esp+32], ERROR_DEVICE
1216
        mov     esi, edx
1216
	mov	esi, edx
1217
        call    disk_dereference
1217
	call	disk_dereference
1218
        ret
1218
	ret
1219
 
1219
 
1220
; This function is called from file_system_lfn.
1220
; This function is called from file_system_lfn.
1221
; This handler is called when virtual root is enumerated
1221
; This handler is called when virtual root is enumerated
1222
; and must return all items which can be handled by this.
1222
; and must return all items which can be handled by this.
1223
; It is called several times, first time with eax=0
1223
; It is called several times, first time with eax=0
1224
; in: eax = 0 for first call, previously returned value for subsequent calls
1224
; in: eax = 0 for first call, previously returned value for subsequent calls
1225
; out: eax = 0 => no more items
1225
; out: eax = 0 => no more items
1226
;      eax != 0 => buffer pointed to by edi contains name of item
1226
;      eax != 0 => buffer pointed to by edi contains name of item
1227
dyndisk_enum_root:
1227
dyndisk_enum_root:
1228
        push    edx             ; save register used in file_system_lfn
1228
	push	edx		; save register used in file_system_lfn
1229
        mov     ecx, disk_list_mutex    ; it will be useful
1229
	mov	ecx, disk_list_mutex	; it will be useful
1230
; 1. If this is the first call, acquire the mutex and initialize.
1230
; 1. If this is the first call, acquire the mutex and initialize.
1231
        test    eax, eax
1231
	test	eax, eax
1232
        jnz     .notfirst
1232
	jnz	.notfirst
1233
        call    mutex_lock
1233
	call	mutex_lock
1234
        mov     eax, disk_list
1234
	mov	eax, disk_list
1235
.notfirst:
1235
.notfirst:
1236
; 2. Get next item.
1236
; 2. Get next item.
1237
        mov     eax, [eax+DISK.Next]
1237
	mov	eax, [eax+DISK.Next]
1238
; 3. If there are no more items, go to 6.
1238
; 3. If there are no more items, go to 6.
1239
        cmp     eax, disk_list
1239
	cmp	eax, disk_list
1240
        jz      .last
1240
	jz	.last
1241
; 4. Copy name from the DISK structure to edi.
1241
; 4. Copy name from the DISK structure to edi.
1242
        push    eax esi
1242
	push	eax esi
1243
        mov     esi, [eax+DISK.Name]
1243
	mov	esi, [eax+DISK.Name]
1244
@@:
1244
@@:
1245
        lodsb
1245
	lodsb
1246
        stosb
1246
	stosb
1247
        test    al, al
1247
	test	al, al
1248
        jnz     @b
1248
	jnz	@b
1249
        pop     esi eax
1249
	pop	esi eax
1250
; 5. Return with eax = item.
1250
; 5. Return with eax = item.