Subversion Repositories Kolibri OS

Rev

Rev 4465 | Rev 5095 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4465 Rev 5089
1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
2
;;                                                              ;;
3
;; Copyright (C) KolibriOS team 2011-2014. All rights reserved. ;;
3
;; Copyright (C) KolibriOS team 2011-2014. 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7
 
7
 
8
$Revision: 4465 $
8
$Revision: 5089 $
9
 
9
 
10
; Read/write functions try to do large operations,
10
; Read/write functions try to do large operations,
11
; it is significantly faster than several small operations.
11
; it is significantly faster than several small operations.
12
; This requires large buffers.
12
; This requires large buffers.
13
; We can't use input/output buffers directly - they can be controlled
13
; We can't use input/output buffers directly - they can be controlled
14
; by user-mode application, so they can be modified between the operation
14
; by user-mode application, so they can be modified between the operation
15
; and copying to/from cache, giving invalid data in cache.
15
; and copying to/from cache, giving invalid data in cache.
16
; It is unclear how to use cache directly, currently cache items are
16
; It is unclear how to use cache directly, currently cache items are
17
; allocated/freed sector-wise, so items for sequential sectors can be
17
; allocated/freed sector-wise, so items for sequential sectors can be
18
; scattered over all the cache.
18
; scattered over all the cache.
19
; So read/write functions allocate a temporary buffer which is
19
; So read/write functions allocate a temporary buffer which is
20
; 1) not greater than half of free memory and
20
; 1) not greater than half of free memory and
21
; 2) not greater than the following constant.
21
; 2) not greater than the following constant.
22
CACHE_MAX_ALLOC_SIZE = 4 shl 20
22
CACHE_MAX_ALLOC_SIZE = 4 shl 20
23
 
23
 
24
; Legacy interface for filesystems fs_{read,write}32_{sys,app}
24
; Legacy interface for filesystems fs_{read,write}32_{sys,app}
25
; gives only one sector for FS. However, per-sector reading is inefficient,
25
; gives only one sector for FS. However, per-sector reading is inefficient,
26
; so internally fs_read32_{sys,app} reads to the cache several sequential
26
; so internally fs_read32_{sys,app} reads to the cache several sequential
27
; sectors, hoping that they will be useful.
27
; sectors, hoping that they will be useful.
28
; Total number of sectors is given by the following constant.
28
; Total number of sectors is given by the following constant.
29
CACHE_LEGACY_READ_SIZE = 16
29
CACHE_LEGACY_READ_SIZE = 16
30
 
30
 
31
; This structure describes one item in the cache.
31
; This structure describes one item in the cache.
32
struct CACHE_ITEM
32
struct CACHE_ITEM
33
SectorLo        dd      ?       ; low 32 bits of sector
33
SectorLo        dd      ?       ; low 32 bits of sector
34
SectorHi        dd      ?       ; high 32 bits of sector
34
SectorHi        dd      ?       ; high 32 bits of sector
35
Status          dd      ?       ; one of CACHE_ITEM_*
35
Status          dd      ?       ; one of CACHE_ITEM_*
36
ends
36
ends
37
 
37
 
38
; Possible values for CACHE_ITEM_*
38
; Possible values for CACHE_ITEM_*
39
CACHE_ITEM_EMPTY = 0
39
CACHE_ITEM_EMPTY = 0
40
CACHE_ITEM_COPY = 1
40
CACHE_ITEM_COPY = 1
41
CACHE_ITEM_MODIFIED = 2
41
CACHE_ITEM_MODIFIED = 2
42
 
42
 
43
; Read several sequential sectors using cache #1.
43
; Read several sequential sectors using cache #1.
44
; in: edx:eax = start sector, relative to start of partition
44
; in: edx:eax = start sector, relative to start of partition
45
; in: ecx = number of sectors to read
45
; in: ecx = number of sectors to read
46
; in: ebx -> buffer
46
; in: ebx -> buffer
47
; in: ebp -> PARTITION
47
; in: ebp -> PARTITION
48
; out: eax = error code, 0 = ok
48
; out: eax = error code, 0 = ok
49
; out: ecx = number of sectors that were read
49
; out: ecx = number of sectors that were read
50
fs_read64_sys:
50
fs_read64_sys:
51
; Save ebx, set ebx to SysCache and let the common part do its work.
51
; Save ebx, set ebx to SysCache and let the common part do its work.
52
        push    ebx
52
        push    ebx
53
        mov     ebx, [ebp+PARTITION.Disk]
53
        mov     ebx, [ebp+PARTITION.Disk]
54
        add     ebx, DISK.SysCache
54
        add     ebx, DISK.SysCache
55
        jmp     fs_read64_common
55
        jmp     fs_read64_common
56
 
56
 
57
; Read several sequential sectors using cache #2.
57
; Read several sequential sectors using cache #2.
58
; in: edx:eax = start sector, relative to start of partition
58
; in: edx:eax = start sector, relative to start of partition
59
; in: ecx = number of sectors to read
59
; in: ecx = number of sectors to read
60
; in: edi -> buffer
60
; in: edi -> buffer
61
; in: ebp -> PARTITION
61
; in: ebp -> PARTITION
62
; out: eax = error code, 0 = ok
62
; out: eax = error code, 0 = ok
63
; out: ecx = number of sectors that were read
63
; out: ecx = number of sectors that were read
64
fs_read64_app:
64
fs_read64_app:
65
; Save ebx, set ebx to AppCache and let the common part do its work.
65
; Save ebx, set ebx to AppCache and let the common part do its work.
66
        push    ebx
66
        push    ebx
67
        mov     ebx, [ebp+PARTITION.Disk]
67
        mov     ebx, [ebp+PARTITION.Disk]
68
        add     ebx, DISK.AppCache
68
        add     ebx, DISK.AppCache
69
 
69
 
70
; Common part of fs_read64_{app,sys}:
70
; Common part of fs_read64_{app,sys}:
71
; read several sequential sectors using the given cache.
71
; read several sequential sectors using the given cache.
72
fs_read64_common:
72
fs_read64_common:
73
; 1. Setup stack frame.
73
; 1. Setup stack frame.
74
        push    esi edi         ; save used registers to be stdcall
74
        push    esi edi         ; save used registers to be stdcall
75
        push    0               ; initialize .error_code
75
        push    0               ; initialize .error_code
76
        push    ebx edx eax ecx ecx     ; initialize stack variables
76
        push    ebx edx eax ecx ecx     ; initialize stack variables
77
virtual at esp
77
virtual at esp
78
.local_vars:
78
.local_vars:
79
.num_sectors_orig dd    ?
79
.num_sectors_orig dd    ?
80
; Number of sectors that should be read. Used to generate output value of ecx.
80
; Number of sectors that should be read. Used to generate output value of ecx.
81
.num_sectors    dd      ?
81
.num_sectors    dd      ?
82
; Number of sectors that remain to be read. Decreases from .num_sectors_orig to 0.
82
; Number of sectors that remain to be read. Decreases from .num_sectors_orig to 0.
83
.sector_lo      dd      ?       ; low 32 bits of the current sector
83
.sector_lo      dd      ?       ; low 32 bits of the current sector
84
.sector_hi      dd      ?       ; high 32 bits of the current sector
84
.sector_hi      dd      ?       ; high 32 bits of the current sector
85
.cache          dd      ?       ; pointer to DISKCACHE
85
.cache          dd      ?       ; pointer to DISKCACHE
86
.error_code     dd      ?       ; current status
86
.error_code     dd      ?       ; current status
87
.local_vars_size = $ - .local_vars
87
.local_vars_size = $ - .local_vars
88
.saved_regs     rd      2
88
.saved_regs     rd      2
89
.buffer         dd      ?       ; filled by fs_read64_{sys,app}
89
.buffer         dd      ?       ; filled by fs_read64_{sys,app}
90
end virtual
90
end virtual
91
; 2. Validate parameters against partition length:
91
; 2. Validate parameters against partition length:
92
; immediately return error if edx:eax are beyond partition end,
92
; immediately return error if edx:eax are beyond partition end,
93
; decrease .num_sectors and .num_sectors_orig, if needed,
93
; decrease .num_sectors and .num_sectors_orig, if needed,
94
; so that the entire operation fits in the partition limits.
94
; so that the entire operation fits in the partition limits.
95
        mov     eax, dword [ebp+PARTITION.Length]
95
        mov     eax, dword [ebp+PARTITION.Length]
96
        mov     edx, dword [ebp+PARTITION.Length+4]
96
        mov     edx, dword [ebp+PARTITION.Length+4]
97
        sub     eax, [.sector_lo]
97
        sub     eax, [.sector_lo]
98
        sbb     edx, [.sector_hi]
98
        sbb     edx, [.sector_hi]
99
        jb      .end_of_media
99
        jb      .end_of_media
100
        jnz     .no_end_of_media
100
        jnz     .no_end_of_media
101
        cmp     ecx, eax
101
        cmp     ecx, eax
102
        jbe     .no_end_of_media
102
        jbe     .no_end_of_media
103
; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA;
103
; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA;
104
; if all subsequent operations would be successful, this would become the final
104
; if all subsequent operations would be successful, this would become the final
105
; status, otherwise this would be rewritten by failed operation.
105
; status, otherwise this would be rewritten by failed operation.
106
        mov     [.num_sectors], eax
106
        mov     [.num_sectors], eax
107
        mov     [.num_sectors_orig], eax
107
        mov     [.num_sectors_orig], eax
108
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
108
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
109
.no_end_of_media:
109
.no_end_of_media:
110
; 3. If number of sectors to read is zero, either because zero-sectors operation
110
; 3. If number of sectors to read is zero, either because zero-sectors operation
111
; was requested or because it got decreased to zero due to partition limits,
111
; was requested or because it got decreased to zero due to partition limits,
112
; just return the current status.
112
; just return the current status.
113
        cmp     [.num_sectors], 0
113
        cmp     [.num_sectors], 0
114
        jz      .return
114
        jz      .return
115
; 4. Shift sector from partition-relative to absolute.
115
; 4. Shift sector from partition-relative to absolute.
116
        mov     eax, dword [ebp+PARTITION.FirstSector]
116
        mov     eax, dword [ebp+PARTITION.FirstSector]
117
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
117
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
118
        add     [.sector_lo], eax
118
        add     [.sector_lo], eax
119
        adc     [.sector_hi], edx
119
        adc     [.sector_hi], edx
120
; 5. If the cache is disabled, pass the request directly to the driver.
120
; 5. If the cache is disabled, pass the request directly to the driver.
121
        mov     edi, [.buffer]
-
 
122
        cmp     [ebx+DISKCACHE.pointer], 0
121
        cmp     [ebx+DISKCACHE.pointer], 0
123
        jz      .nocache
122
        jz      .nocache
124
; 6. Look for sectors in the cache, sequentially from the beginning.
123
; 6. Look for sectors in the cache, sequentially from the beginning.
125
; Stop at the first sector that is not in the cache
124
; Stop at the first sector that is not in the cache
126
; or when all sectors were read from the cache.
125
; or when all sectors were read from the cache.
127
; 6a. Acquire the lock.
126
; 6a. Acquire the lock.
128
        mov     ecx, [ebp+PARTITION.Disk]
127
        mov     ecx, [ebp+PARTITION.Disk]
129
        add     ecx, DISK.CacheLock
128
        add     ecx, DISK.CacheLock
130
        call    mutex_lock
129
        call    mutex_lock
131
.lookup_in_cache_loop:
130
.lookup_in_cache_loop:
132
; 6b. For each sector, call the lookup function without adding to the cache.
131
; 6b. For each sector, call the lookup function without adding to the cache.
133
        mov     eax, [.sector_lo]
132
        mov     eax, [.sector_lo]
134
        mov     edx, [.sector_hi]
133
        mov     edx, [.sector_hi]
135
        call    cache_lookup_read
134
        call    cache_lookup_read
136
; 6c. If it has failed, the sector is not in cache;
135
; 6c. If it has failed, the sector is not in cache;
137
; release the lock and go to 7.
136
; release the lock and go to 7.
138
        jc      .not_found_in_cache
137
        jc      .not_found_in_cache
139
; The sector is found in cache.
138
; The sector is found in cache.
140
; 6d. Copy data for the caller.
139
; 6d. Copy data for the caller, advance [.buffer].
-
 
140
        mov     esi, edi
141
; Note that buffer in edi is advanced automatically.
141
        mov     edi, [.buffer]
142
        mov     esi, ecx
142
        mov     eax, 1
143
        shl     esi, 9
143
        shl     eax, cl
144
        add     esi, [ebx+DISKCACHE.data]
144
        mov     ecx, eax
145
        mov     ecx, 512/4
145
        shr     ecx, 2
146
        rep movsd
146
        rep movsd
-
 
147
        mov     [.buffer], edi
147
; 6e. Advance the sector.
148
; 6e. Advance the sector.
148
        add     [.sector_lo], 1
149
        add     [.sector_lo], 1
149
        adc     [.sector_hi], 0
150
        adc     [.sector_hi], 0
150
; 6f. Decrement number of sectors left.
151
; 6f. Decrement number of sectors left.
151
; If all sectors were read, release the lock and return.
152
; If all sectors were read, release the lock and return.
152
        dec     [.num_sectors]
153
        dec     [.num_sectors]
153
        jnz     .lookup_in_cache_loop
154
        jnz     .lookup_in_cache_loop
154
; Release the lock acquired at 6a.
155
; Release the lock acquired at 6a.
155
        mov     ecx, [ebp+PARTITION.Disk]
156
        mov     ecx, [ebp+PARTITION.Disk]
156
        add     ecx, DISK.CacheLock
157
        add     ecx, DISK.CacheLock
157
        call    mutex_unlock
158
        call    mutex_unlock
158
.return:
159
.return:
159
        mov     eax, [.error_code]
160
        mov     eax, [.error_code]
160
        mov     ecx, [.num_sectors_orig]
161
        mov     ecx, [.num_sectors_orig]
161
        sub     ecx, [.num_sectors]
162
        sub     ecx, [.num_sectors]
162
.nothing:
163
.nothing:
163
        add     esp, .local_vars_size
164
        add     esp, .local_vars_size
164
        pop     edi esi ebx     ; restore used registers to be stdcall
165
        pop     edi esi ebx     ; restore used registers to be stdcall
165
        ret
166
        ret
166
.not_found_in_cache:
167
.not_found_in_cache:
167
; Release the lock acquired at 6a.
168
; Release the lock acquired at 6a.
168
        mov     ecx, [ebp+PARTITION.Disk]
169
        mov     ecx, [ebp+PARTITION.Disk]
169
        add     ecx, DISK.CacheLock
170
        add     ecx, DISK.CacheLock
170
        call    mutex_unlock
171
        call    mutex_unlock
171
; The current sector is not present in the cache.
172
; The current sector is not present in the cache.
172
; Ask the driver to read all requested not-yet-read sectors,
173
; Ask the driver to read all requested not-yet-read sectors,
173
; put results in the cache.
174
; put results in the cache.
174
; Also, see the comment before the definition of CACHE_MAX_ALLOC_SIZE.
175
; Also, see the comment before the definition of CACHE_MAX_ALLOC_SIZE.
175
; 7. Allocate buffer for operations.
176
; 7. Allocate buffer for operations.
176
; Normally, create buffer that is sufficient for all remaining data.
177
; Normally, create buffer that is sufficient for all remaining data.
177
; However, for extra-large requests make an upper limit:
178
; However, for extra-large requests make an upper limit:
178
; do not use more than half of the free memory
179
; do not use more than half of the free memory
179
; or more than CACHE_MAX_ALLOC_SIZE bytes.
180
; or more than CACHE_MAX_ALLOC_SIZE bytes.
-
 
181
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
180
        mov     ebx, [pg_data.pages_free]
182
        mov     ebx, [pg_data.pages_free]
181
        shr     ebx, 1
183
        shr     ebx, 1
182
        jz      .nomemory
184
        jz      .nomemory
183
        cmp     ebx, CACHE_MAX_ALLOC_SIZE shr 12
185
        cmp     ebx, CACHE_MAX_ALLOC_SIZE shr 12
184
        jbe     @f
186
        jbe     @f
185
        mov     ebx, CACHE_MAX_ALLOC_SIZE shr 12
187
        mov     ebx, CACHE_MAX_ALLOC_SIZE shr 12
186
@@:
188
@@:
187
        shl     ebx, 12 - 9
189
        shl     ebx, 12
-
 
190
        shr     ebx, cl
-
 
191
        jz      .nomemory
188
        cmp     ebx, [.num_sectors]
192
        cmp     ebx, [.num_sectors]
189
        jbe     @f
193
        jbe     @f
190
        mov     ebx, [.num_sectors]
194
        mov     ebx, [.num_sectors]
191
@@:
195
@@:
192
        mov     eax, ebx
196
        mov     eax, ebx
193
        shl     eax, 9
197
        shl     eax, cl
194
        stdcall kernel_alloc, eax
198
        stdcall kernel_alloc, eax
195
; If failed, return the appropriate error code.
199
; If failed, return the appropriate error code.
196
        test    eax, eax
200
        test    eax, eax
197
        jz      .nomemory
201
        jz      .nomemory
198
        mov     esi, eax
202
        mov     esi, eax
199
; Split the request to chunks that fit in the allocated buffer.
203
; Split the request to chunks that fit in the allocated buffer.
200
.read_loop:
204
.read_loop:
201
; 8. Get iteration size: either size of allocated buffer in sectors
205
; 8. Get iteration size: either size of allocated buffer in sectors
202
; or number of sectors left, select what is smaller.
206
; or number of sectors left, select what is smaller.
203
        cmp     ebx, [.num_sectors]
207
        cmp     ebx, [.num_sectors]
204
        jbe     @f
208
        jbe     @f
205
        mov     ebx, [.num_sectors]
209
        mov     ebx, [.num_sectors]
206
@@:
210
@@:
207
; 9. Create second portion of local variables.
211
; 9. Create second portion of local variables.
208
; Note that variables here and above are esp-relative;
212
; Note that variables here and above are esp-relative;
209
; it means that all addresses should be corrected when esp is changing.
213
; it means that all addresses should be corrected when esp is changing.
210
        push    ebx esi esi
214
        push    ebx esi esi
211
        push    ebx
215
        push    ebx
212
; In particular, num_sectors is now [.num_sectors+.local_vars2_size].
216
; In particular, num_sectors is now [.num_sectors+.local_vars2_size].
213
virtual at esp
217
virtual at esp
214
.local_vars2:
218
.local_vars2:
215
.current_num_sectors    dd      ?       ; number of sectors that were read
219
.current_num_sectors    dd      ?       ; number of sectors that were read
216
.current_buffer         dd      ?
220
.current_buffer         dd      ?
217
; pointer inside .allocated_buffer that points
221
; pointer inside .allocated_buffer that points
218
; to the beginning of not-processed data
222
; to the beginning of not-processed data
219
.allocated_buffer       dd      ?       ; saved in safe place
223
.allocated_buffer       dd      ?       ; saved in safe place
220
.iteration_size         dd      ?       ; saved in safe place
224
.iteration_size         dd      ?       ; saved in safe place
221
.local_vars2_size = $ - .local_vars2
225
.local_vars2_size = $ - .local_vars2
222
end virtual
226
end virtual
223
; 10. Call the driver, reading the next chunk.
227
; 10. Call the driver, reading the next chunk.
224
        push    esp     ; numsectors
228
        push    esp     ; numsectors
225
        push    [.sector_hi+.local_vars2_size+4] ; startsector
229
        push    [.sector_hi+.local_vars2_size+4] ; startsector
226
        push    [.sector_lo+.local_vars2_size+8] ; startsector
230
        push    [.sector_lo+.local_vars2_size+8] ; startsector
227
        push    esi     ; buffer
231
        push    esi     ; buffer
228
        mov     esi, [ebp+PARTITION.Disk]
232
        mov     esi, [ebp+PARTITION.Disk]
229
        mov     al, DISKFUNC.read
233
        mov     al, DISKFUNC.read
230
        call    disk_call_driver
234
        call    disk_call_driver
231
; If failed, save error code.
235
; If failed, save error code.
232
        test    eax, eax
236
        test    eax, eax
233
        jz      @f
237
        jz      @f
234
        mov     [.error_code+.local_vars2_size], eax
238
        mov     [.error_code+.local_vars2_size], eax
235
@@:
239
@@:
236
; 11. Copy data for the caller.
240
; 11. Copy data for the caller, advance .buffer.
237
; Note that buffer in edi is advanced automatically.
-
 
238
        cmp     [.current_num_sectors], 0
241
        cmp     [.current_num_sectors], 0
239
        jz      .copy_done
242
        jz      .copy_done
-
 
243
        mov     ebx, [.cache+.local_vars2_size]
240
        mov     ecx, [.current_num_sectors]
244
        mov     eax, [.current_num_sectors]
-
 
245
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
241
        shl     ecx, 9-2
246
        shl     eax, cl
242
        mov     esi, [.allocated_buffer]
247
        mov     esi, [.allocated_buffer]
-
 
248
        mov     edi, [.buffer+.local_vars2_size]
-
 
249
        mov     ecx, eax
-
 
250
        shr     ecx, 2
243
        rep movsd
251
        rep movsd
-
 
252
        mov     [.buffer+.local_vars2_size], edi
244
; 12. Copy data to the cache.
253
; 12. Copy data to the cache.
245
; 12a. Acquire the lock.
254
; 12a. Acquire the lock.
246
        mov     ebx, [.cache+.local_vars2_size]
-
 
247
        mov     ecx, [ebp+PARTITION.Disk]
255
        mov     ecx, [ebp+PARTITION.Disk]
248
        add     ecx, DISK.CacheLock
256
        add     ecx, DISK.CacheLock
249
        call    mutex_lock
257
        call    mutex_lock
250
; 12b. Prepare for the loop: save edi and create a local variable that
258
; 12b. Prepare for the loop: create a local variable that
251
; stores number of sectors to be copied.
259
; stores number of sectors to be copied.
252
        push    edi
-
 
253
        push    [.current_num_sectors+4]
260
        push    [.current_num_sectors]
254
.store_to_cache:
261
.store_to_cache:
255
; 12c. For each sector, call the lookup function with adding to the cache, if not yet.
262
; 12c. For each sector, call the lookup function with adding to the cache, if not yet.
256
        mov     eax, [.sector_lo+.local_vars2_size+8]
263
        mov     eax, [.sector_lo+.local_vars2_size+4]
257
        mov     edx, [.sector_hi+.local_vars2_size+8]
264
        mov     edx, [.sector_hi+.local_vars2_size+4]
258
        call    cache_lookup_write
265
        call    cache_lookup_write
259
        test    eax, eax
266
        test    eax, eax
260
        jnz     .cache_error
267
        jnz     .cache_error
261
; 12d. If the sector was already present in the cache as modified,
268
; 12d. If the sector was already present in the cache as modified,
262
; data that were read at step 10 for this sector are obsolete,
269
; data that were read at step 10 for this sector are obsolete,
263
; so rewrite data for the caller from the cache.
270
; so rewrite data for the caller from the cache.
264
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
271
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
265
        jnz     .not_modified
272
        jnz     .not_modified
266
        mov     esi, ecx
273
        mov     esi, edi
-
 
274
        mov     edi, [.buffer+.local_vars2_size+4]
267
        shl     esi, 9
275
        mov     eax, [esp]
268
        add     esi, [ebx+DISKCACHE.data]
276
        shl     eax, cl
269
        mov     edi, [esp+4]
277
        sub     edi, eax
270
        mov     ecx, [esp]
278
        mov     eax, 1
271
        shl     ecx, 9-2
279
        shl     eax, cl
272
        sub     edi, ecx
280
        mov     ecx, eax
273
        mov     ecx, 512/4
281
        shr     ecx, 2
274
        rep movsd
282
        rep movsd
275
        add     [.current_buffer+8], 512
283
        add     [.current_buffer+4], eax
276
        jmp     .sector_done
284
        jmp     .sector_done
277
.not_modified:
285
.not_modified:
278
; 12e. For each not-modified sector,
286
; 12e. For each not-modified sector,
279
; copy data, mark the item as not-modified copy of the disk,
287
; copy data, mark the item as not-modified copy of the disk,
280
; advance .current_buffer and .sector_hi:.sector_lo to the next sector.
288
; advance .current_buffer and .sector_hi:.sector_lo to the next sector.
281
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
289
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
282
        mov     esi, [.current_buffer+8]
290
        mov     eax, 1
283
        mov     edi, ecx
291
        shl     eax, cl
284
        shl     edi, 9
292
        mov     esi, [.current_buffer+4]
285
        add     edi, [ebx+DISKCACHE.data]
293
        mov     ecx, eax
286
        mov     ecx, 512/4
294
        shr     ecx, 2
287
        rep movsd
295
        rep movsd
288
        mov     [.current_buffer+8], esi
296
        mov     [.current_buffer+4], esi
289
.sector_done:
297
.sector_done:
290
        add     [.sector_lo+.local_vars2_size+8], 1
298
        add     [.sector_lo+.local_vars2_size+4], 1
291
        adc     [.sector_hi+.local_vars2_size+8], 0
299
        adc     [.sector_hi+.local_vars2_size+4], 0
292
; 12f. Continue the loop 12c-12e until all sectors are read.
300
; 12f. Continue the loop 12c-12e until all sectors are read.
293
        dec     dword [esp]
301
        dec     dword [esp]
294
        jnz     .store_to_cache
302
        jnz     .store_to_cache
295
.cache_error:
303
.cache_error:
296
; 12g. Restore after the loop: pop the local variable and restore edi.
304
; 12g. Restore after the loop: pop the local variable.
297
        pop     ecx
305
        pop     ecx
298
        pop     edi
-
 
299
; 12h. Release the lock.
306
; 12h. Release the lock.
300
        mov     ecx, [ebp+PARTITION.Disk]
307
        mov     ecx, [ebp+PARTITION.Disk]
301
        add     ecx, DISK.CacheLock
308
        add     ecx, DISK.CacheLock
302
        call    mutex_unlock
309
        call    mutex_unlock
303
.copy_done:
310
.copy_done:
304
; 13. Remove portion of local variables created at step 9.
311
; 13. Remove portion of local variables created at step 9.
305
        pop     ecx
312
        pop     ecx
306
        pop     esi esi ebx
313
        pop     esi esi ebx
307
; 14. Continue iterations while number of sectors read by the driver
314
; 14. Continue iterations while number of sectors read by the driver
308
; is equal to number of sectors requested and there are additional sectors.
315
; is equal to number of sectors requested and there are additional sectors.
309
        cmp     ecx, ebx
316
        cmp     ecx, ebx
310
        jnz     @f
317
        jnz     @f
311
        sub     [.num_sectors], ebx
318
        sub     [.num_sectors], ebx
312
        jnz     .read_loop
319
        jnz     .read_loop
313
@@:
320
@@:
314
; 15. Free the buffer allocated at step 7 and return.
321
; 15. Free the buffer allocated at step 7 and return.
315
        stdcall kernel_free, esi
322
        stdcall kernel_free, esi
316
        jmp     .return
323
        jmp     .return
317
 
324
 
318
; Special branches:
325
; Special branches:
319
.nomemory:
326
.nomemory:
320
; memory allocation failed at step 7: return the corresponding error
327
; memory allocation failed at step 7: return the corresponding error
321
        mov     [.error_code], DISK_STATUS_NO_MEMORY
328
        mov     [.error_code], DISK_STATUS_NO_MEMORY
322
        jmp     .return
329
        jmp     .return
323
.nocache:
330
.nocache:
324
; step 5, after correcting number of sectors to fit in partition limits
331
; step 5, after correcting number of sectors to fit in partition limits
325
; and advancing partition-relative sector to absolute,
332
; and advancing partition-relative sector to absolute,
326
; sees that cache is disabled: pass corrected request to the driver
333
; sees that cache is disabled: pass corrected request to the driver
327
        lea     eax, [.num_sectors]
334
        lea     eax, [.num_sectors]
328
        push    eax             ; numsectors
335
        push    eax             ; numsectors
329
        push    [.sector_hi+4]  ; startsector
336
        push    [.sector_hi+4]  ; startsector
330
        push    [.sector_lo+8]  ; startsector
337
        push    [.sector_lo+8]  ; startsector
331
        push    edi     ; buffer
338
        push    [.buffer+12]    ; buffer
332
        mov     esi, [ebp+PARTITION.Disk]
339
        mov     esi, [ebp+PARTITION.Disk]
333
        mov     al, DISKFUNC.read
340
        mov     al, DISKFUNC.read
334
        call    disk_call_driver
341
        call    disk_call_driver
335
        test    eax, eax
342
        test    eax, eax
336
        jnz     @f
343
        jnz     @f
337
        mov     eax, [.error_code]
344
        mov     eax, [.error_code]
338
@@:
345
@@:
339
        mov     ecx, [.num_sectors]
346
        mov     ecx, [.num_sectors]
340
        jmp     .nothing
347
        jmp     .nothing
341
.end_of_media:
348
.end_of_media:
342
; requested sector is beyond the partition end: return the corresponding error
349
; requested sector is beyond the partition end: return the corresponding error
343
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
350
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
344
        jmp     .return
351
        jmp     .return
345
 
352
 
346
; Write several sequential sectors using cache #1.
353
; Write several sequential sectors using cache #1.
347
; in: edx:eax = start sector
354
; in: edx:eax = start sector
348
; in: ecx = number of sectors to write
355
; in: ecx = number of sectors to write
349
; in: ebx -> buffer
356
; in: ebx -> buffer
350
; in: ebp -> PARTITION
357
; in: ebp -> PARTITION
351
; out: eax = error code, 0 = ok
358
; out: eax = error code, 0 = ok
352
; out: ecx = number of sectors that were written
359
; out: ecx = number of sectors that were written
353
fs_write64_sys:
360
fs_write64_sys:
354
; Save ebx, set ebx to SysCache and let the common part do its work.
361
; Save ebx, set ebx to SysCache and let the common part do its work.
355
        push    ebx
362
        push    ebx
356
        mov     ebx, [ebp+PARTITION.Disk]
363
        mov     ebx, [ebp+PARTITION.Disk]
357
        add     ebx, DISK.SysCache
364
        add     ebx, DISK.SysCache
358
        jmp     fs_write64_common
365
        jmp     fs_write64_common
359
 
366
 
360
; Write several sequential sectors using cache #2.
367
; Write several sequential sectors using cache #2.
361
; in: edx:eax = start sector
368
; in: edx:eax = start sector
362
; in: ecx = number of sectors to write
369
; in: ecx = number of sectors to write
363
; in: ebx -> buffer
370
; in: ebx -> buffer
364
; in: ebp -> PARTITION
371
; in: ebp -> PARTITION
365
; out: eax = error code, 0 = ok
372
; out: eax = error code, 0 = ok
366
; out: ecx = number of sectors that were written
373
; out: ecx = number of sectors that were written
367
fs_write64_app:
374
fs_write64_app:
368
; Save ebx, set ebx to AppCache and let the common part do its work.
375
; Save ebx, set ebx to AppCache and let the common part do its work.
369
        push    ebx
376
        push    ebx
370
        mov     ebx, [ebp+PARTITION.Disk]
377
        mov     ebx, [ebp+PARTITION.Disk]
371
        add     ebx, DISK.AppCache
378
        add     ebx, DISK.AppCache
372
 
379
 
373
; Common part of fs_write64_{app,sys}:
380
; Common part of fs_write64_{app,sys}:
374
; write several sequential sectors using the given cache.
381
; write several sequential sectors using the given cache.
375
fs_write64_common:
382
fs_write64_common:
376
; 1. Setup stack frame.
383
; 1. Setup stack frame.
377
        push    esi edi         ; save used registers to be stdcall
384
        push    esi edi         ; save used registers to be stdcall
378
        push    0               ; initialize .error_code
385
        push    0               ; initialize .error_code
379
        push    edx eax ecx ecx ; initialize stack variables
386
        push    edx eax ecx ecx ; initialize stack variables
380
        push    [.buffer-4]     ; copy [.buffer] to [.cur_buffer]
387
        push    [.buffer-4]     ; copy [.buffer] to [.cur_buffer]
381
                                ; -4 is due to esp-relative addressing
388
                                ; -4 is due to esp-relative addressing
382
virtual at esp
389
virtual at esp
383
.local_vars:
390
.local_vars:
384
.cur_buffer     dd      ?       ; pointer to data that are currently copying
391
.cur_buffer     dd      ?       ; pointer to data that are currently copying
385
.num_sectors_orig dd    ?
392
.num_sectors_orig dd    ?
386
; Number of sectors that should be written. Used to generate output value of ecx.
393
; Number of sectors that should be written. Used to generate output value of ecx.
387
.num_sectors    dd      ?
394
.num_sectors    dd      ?
388
; Number of sectors that remain to be written.
395
; Number of sectors that remain to be written.
389
.sector_lo      dd      ?       ; low 32 bits of the current sector
396
.sector_lo      dd      ?       ; low 32 bits of the current sector
390
.sector_hi      dd      ?       ; high 32 bits of the current sector
397
.sector_hi      dd      ?       ; high 32 bits of the current sector
391
.error_code     dd      ?       ; current status
398
.error_code     dd      ?       ; current status
392
.local_vars_size = $ - .local_vars
399
.local_vars_size = $ - .local_vars
393
.saved_regs     rd      2
400
.saved_regs     rd      2
394
.buffer         dd      ?       ; filled by fs_write64_{sys,app}
401
.buffer         dd      ?       ; filled by fs_write64_{sys,app}
395
end virtual
402
end virtual
396
; 2. Validate parameters against partition length:
403
; 2. Validate parameters against partition length:
397
; immediately return error if edx:eax are beyond partition end,
404
; immediately return error if edx:eax are beyond partition end,
398
; decrease .num_sectors and .num_sectors_orig, if needed,
405
; decrease .num_sectors and .num_sectors_orig, if needed,
399
; so that the entire operation fits in the partition limits.
406
; so that the entire operation fits in the partition limits.
400
        mov     eax, dword [ebp+PARTITION.Length]
407
        mov     eax, dword [ebp+PARTITION.Length]
401
        mov     edx, dword [ebp+PARTITION.Length+4]
408
        mov     edx, dword [ebp+PARTITION.Length+4]
402
        sub     eax, [.sector_lo]
409
        sub     eax, [.sector_lo]
403
        sbb     edx, [.sector_hi]
410
        sbb     edx, [.sector_hi]
404
        jb      .end_of_media
411
        jb      .end_of_media
405
        jnz     .no_end_of_media
412
        jnz     .no_end_of_media
406
        cmp     ecx, eax
413
        cmp     ecx, eax
407
        jbe     .no_end_of_media
414
        jbe     .no_end_of_media
408
; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA;
415
; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA;
409
; if all subsequent operations would be successful, this would become the final
416
; if all subsequent operations would be successful, this would become the final
410
; status, otherwise this would be rewritten by failed operation.
417
; status, otherwise this would be rewritten by failed operation.
411
        mov     [.num_sectors], eax
418
        mov     [.num_sectors], eax
412
        mov     [.num_sectors_orig], eax
419
        mov     [.num_sectors_orig], eax
413
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
420
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
414
.no_end_of_media:
421
.no_end_of_media:
415
; 3. If number of sectors to write is zero, either because zero-sectors operation
422
; 3. If number of sectors to write is zero, either because zero-sectors operation
416
; was requested or because it got decreased to zero due to partition limits,
423
; was requested or because it got decreased to zero due to partition limits,
417
; just return the current status.
424
; just return the current status.
418
        cmp     [.num_sectors], 0
425
        cmp     [.num_sectors], 0
419
        jz      .return
426
        jz      .return
420
; 4. Shift sector from partition-relative to absolute.
427
; 4. Shift sector from partition-relative to absolute.
421
        mov     eax, dword [ebp+PARTITION.FirstSector]
428
        mov     eax, dword [ebp+PARTITION.FirstSector]
422
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
429
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
423
        add     [.sector_lo], eax
430
        add     [.sector_lo], eax
424
        adc     [.sector_hi], edx
431
        adc     [.sector_hi], edx
425
; 5. If the cache is disabled, pass the request directly to the driver.
432
; 5. If the cache is disabled, pass the request directly to the driver.
426
        cmp     [ebx+DISKCACHE.pointer], 0
433
        cmp     [ebx+DISKCACHE.pointer], 0
427
        jz      .nocache
434
        jz      .nocache
428
; 6. Store sectors in the cache, sequentially from the beginning.
435
; 6. Store sectors in the cache, sequentially from the beginning.
429
; 6a. Acquire the lock.
436
; 6a. Acquire the lock.
430
        mov     ecx, [ebp+PARTITION.Disk]
437
        mov     ecx, [ebp+PARTITION.Disk]
431
        add     ecx, DISK.CacheLock
438
        add     ecx, DISK.CacheLock
432
        call    mutex_lock
439
        call    mutex_lock
433
.lookup_in_cache_loop:
440
.lookup_in_cache_loop:
434
; 6b. For each sector, call the lookup function with adding to the cache, if not yet.
441
; 6b. For each sector, call the lookup function with adding to the cache, if not yet.
435
        mov     eax, [.sector_lo]
442
        mov     eax, [.sector_lo]
436
        mov     edx, [.sector_hi]
443
        mov     edx, [.sector_hi]
437
        call    cache_lookup_write
444
        call    cache_lookup_write
438
        test    eax, eax
445
        test    eax, eax
439
        jnz     .cache_error
446
        jnz     .cache_error
440
; 6c. For each sector, copy data, mark the item as modified and not saved,
447
; 6c. For each sector, copy data, mark the item as modified and not saved,
441
; advance .current_buffer to the next sector.
448
; advance .current_buffer to the next sector.
442
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
449
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
-
 
450
        mov     eax, 1
-
 
451
        shl     eax, cl
443
        mov     esi, [.cur_buffer]
452
        mov     esi, [.cur_buffer]
444
        mov     edi, ecx
453
        mov     ecx, eax
445
        shl     edi, 9
454
        shr     ecx, 2
446
        add     edi, [ebx+DISKCACHE.data]
-
 
447
        mov     ecx, 512/4
-
 
448
        rep movsd
455
        rep movsd
449
        mov     [.cur_buffer], esi
456
        mov     [.cur_buffer], esi
450
; 6d. Remove the sector from the other cache.
457
; 6d. Remove the sector from the other cache.
451
; Normally it should not be there, but prefetching could put to the app cache
458
; Normally it should not be there, but prefetching could put to the app cache
452
; data that normally should belong to the sys cache and vice versa.
459
; data that normally should belong to the sys cache and vice versa.
453
; Note: this requires that both caches must be protected by the same lock.
460
; Note: this requires that both caches must be protected by the same lock.
454
        mov     eax, [.sector_lo]
461
        mov     eax, [.sector_lo]
455
        mov     edx, [.sector_hi]
462
        mov     edx, [.sector_hi]
456
        push    ebx
463
        push    ebx
457
        sub     ebx, [ebp+PARTITION.Disk]
464
        sub     ebx, [ebp+PARTITION.Disk]
458
        xor     ebx, DISK.SysCache xor DISK.AppCache
465
        xor     ebx, DISK.SysCache xor DISK.AppCache
459
        add     ebx, [ebp+PARTITION.Disk]
466
        add     ebx, [ebp+PARTITION.Disk]
460
        call    cache_lookup_read
467
        call    cache_lookup_read
461
        jc      @f
468
        jc      @f
462
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
469
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
463
@@:
470
@@:
464
        pop     ebx
471
        pop     ebx
465
; 6e. Advance .sector_hi:.sector_lo to the next sector.
472
; 6e. Advance .sector_hi:.sector_lo to the next sector.
466
        add     [.sector_lo], 1
473
        add     [.sector_lo], 1
467
        adc     [.sector_hi], 0
474
        adc     [.sector_hi], 0
468
; 6f. Continue the loop at 6b-6e until all sectors are processed.
475
; 6f. Continue the loop at 6b-6e until all sectors are processed.
469
        dec     [.num_sectors]
476
        dec     [.num_sectors]
470
        jnz     .lookup_in_cache_loop
477
        jnz     .lookup_in_cache_loop
471
.unlock_return:
478
.unlock_return:
472
; 6g. Release the lock and return.
479
; 6g. Release the lock and return.
473
        mov     ecx, [ebp+PARTITION.Disk]
480
        mov     ecx, [ebp+PARTITION.Disk]
474
        add     ecx, DISK.CacheLock
481
        add     ecx, DISK.CacheLock
475
        call    mutex_unlock
482
        call    mutex_unlock
476
.return:
483
.return:
477
        mov     eax, [.error_code]
484
        mov     eax, [.error_code]
478
        mov     ecx, [.num_sectors_orig]
485
        mov     ecx, [.num_sectors_orig]
479
        sub     ecx, [.num_sectors]
486
        sub     ecx, [.num_sectors]
480
.nothing:
487
.nothing:
481
        add     esp, .local_vars_size
488
        add     esp, .local_vars_size
482
        pop     edi esi ebx
489
        pop     edi esi ebx
483
        ret
490
        ret
484
 
491
 
485
; Special branches:
492
; Special branches:
486
.cache_error:
493
.cache_error:
487
; error at flushing the cache while adding sector to the cache:
494
; error at flushing the cache while adding sector to the cache:
488
; return the error from the lookup function
495
; return the error from the lookup function
489
        mov     [.error_code], eax
496
        mov     [.error_code], eax
490
        jmp     .unlock_return
497
        jmp     .unlock_return
491
.end_of_media:
498
.end_of_media:
492
; requested sector is beyond the partition end: return the corresponding error
499
; requested sector is beyond the partition end: return the corresponding error
493
        mov     eax, DISK_STATUS_END_OF_MEDIA
500
        mov     eax, DISK_STATUS_END_OF_MEDIA
494
        xor     ecx, ecx
501
        xor     ecx, ecx
495
        jmp     .nothing
502
        jmp     .nothing
496
.nocache:
503
.nocache:
497
; step 5, after correcting number of sectors to fit in partition limits
504
; step 5, after correcting number of sectors to fit in partition limits
498
; and advancing partition-relative sector to absolute,
505
; and advancing partition-relative sector to absolute,
499
; sees that cache is disabled: pass corrected request to the driver
506
; sees that cache is disabled: pass corrected request to the driver
500
        lea     eax, [.num_sectors]
507
        lea     eax, [.num_sectors]
501
        push    eax             ; numsectors
508
        push    eax             ; numsectors
502
        push    [.sector_hi+4]  ; startsector
509
        push    [.sector_hi+4]  ; startsector
503
        push    [.sector_lo+8]  ; startsector
510
        push    [.sector_lo+8]  ; startsector
504
        push    [.buffer+12]    ; buffer
511
        push    [.buffer+12]    ; buffer
505
        mov     esi, [ebp+PARTITION.Disk]
512
        mov     esi, [ebp+PARTITION.Disk]
506
        mov     al, DISKFUNC.write
513
        mov     al, DISKFUNC.write
507
        call    disk_call_driver
514
        call    disk_call_driver
508
        mov     ecx, [.num_sectors]
515
        mov     ecx, [.num_sectors]
509
        jmp     .nothing
516
        jmp     .nothing
510
 
517
 
511
; Legacy. Use fs_read64_sys instead.
518
; Legacy. Use fs_read64_sys instead.
512
; This function is intended to replace the old 'hd_read' function when
519
; This function is intended to replace the old 'hd_read' function when
513
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
520
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
514
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
521
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
515
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
522
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
516
; eax is relative to partition start
523
; eax is relative to partition start
517
; out: eax = error code; 0 = ok
524
; out: eax = error code; 0 = ok
518
fs_read32_sys:
525
fs_read32_sys:
519
; Save ebx, set ebx to SysCache and let the common part do its work.
526
; Save ebx, set ebx to SysCache and let the common part do its work.
520
        push    ebx
527
        push    ebx
521
        mov     ebx, [ebp+PARTITION.Disk]
528
        mov     ebx, [ebp+PARTITION.Disk]
522
        add     ebx, DISK.SysCache
529
        add     ebx, DISK.SysCache
523
        jmp     fs_read32_common
530
        jmp     fs_read32_common
524
 
531
 
525
; Legacy. Use fs_read64_app instead.
532
; Legacy. Use fs_read64_app instead.
526
; This function is intended to replace the old 'hd_read' function when
533
; This function is intended to replace the old 'hd_read' function when
527
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
534
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
528
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
535
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
529
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
536
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
530
; eax is relative to partition start
537
; eax is relative to partition start
531
; out: eax = error code; 0 = ok
538
; out: eax = error code; 0 = ok
532
fs_read32_app:
539
fs_read32_app:
533
; Save ebx, set ebx to AppCache and let the common part do its work.
540
; Save ebx, set ebx to AppCache and let the common part do its work.
534
        push    ebx
541
        push    ebx
535
        mov     ebx, [ebp+PARTITION.Disk]
542
        mov     ebx, [ebp+PARTITION.Disk]
536
        add     ebx, DISK.AppCache
543
        add     ebx, DISK.AppCache
537
 
544
 
538
; This label is the common part of fs_read32_sys and fs_read32_app.
545
; This label is the common part of fs_read32_sys and fs_read32_app.
539
fs_read32_common:
546
fs_read32_common:
540
; 1. Check that the required sector is inside the partition. If no, return
547
; 1. Check that the required sector is inside the partition. If no, return
541
; DISK_STATUS_END_OF_MEDIA.
548
; DISK_STATUS_END_OF_MEDIA.
542
        cmp     dword [ebp+PARTITION.Length+4], 0
549
        cmp     dword [ebp+PARTITION.Length+4], 0
543
        jnz     @f
550
        jnz     @f
544
        cmp     dword [ebp+PARTITION.Length], eax
551
        cmp     dword [ebp+PARTITION.Length], eax
545
        ja      @f
552
        ja      @f
546
        mov     eax, DISK_STATUS_END_OF_MEDIA
553
        mov     eax, DISK_STATUS_END_OF_MEDIA
547
        pop     ebx
554
        pop     ebx
548
        ret
555
        ret
549
@@:
556
@@:
550
; 2. Get the absolute sector on the disk.
557
; 2. Get the absolute sector on the disk.
551
        push    ecx edx esi edi
558
        push    ecx edx esi edi
552
        xor     edx, edx
559
        xor     edx, edx
553
        add     eax, dword [ebp+PARTITION.FirstSector]
560
        add     eax, dword [ebp+PARTITION.FirstSector]
554
        adc     edx, dword [ebp+PARTITION.FirstSector+4]
561
        adc     edx, dword [ebp+PARTITION.FirstSector+4]
555
; 3. If there is no cache for this disk, just pass the request to the driver.
562
; 3. If there is no cache for this disk, just pass the request to the driver.
556
        cmp     [ebx+DISKCACHE.pointer], 0
563
        cmp     [ebx+DISKCACHE.pointer], 0
557
        jnz     .scancache
564
        jnz     .scancache
558
        push    1
565
        push    1
559
        push    esp     ; numsectors
566
        push    esp     ; numsectors
560
        push    edx     ; startsector
567
        push    edx     ; startsector
561
        push    eax     ; startsector
568
        push    eax     ; startsector
562
        pushd   [esp+32]; buffer
569
        pushd   [esp+32]; buffer
563
        mov     esi, [ebp+PARTITION.Disk]
570
        mov     esi, [ebp+PARTITION.Disk]
564
        mov     al, DISKFUNC.read
571
        mov     al, DISKFUNC.read
565
        call    disk_call_driver
572
        call    disk_call_driver
566
        pop     ecx
573
        pop     ecx
567
        pop     edi esi edx ecx
574
        pop     edi esi edx ecx
568
        pop     ebx
575
        pop     ebx
569
        ret
576
        ret
570
.scancache:
577
.scancache:
571
        push    ebx edx eax
578
        push    ebx edx eax
572
virtual at esp
579
virtual at esp
573
.local_vars:
580
.local_vars:
574
.sector_lo      dd      ?
581
.sector_lo      dd      ?
575
.sector_hi      dd      ?
582
.sector_hi      dd      ?
576
.cache          dd      ?
583
.cache          dd      ?
577
.local_vars_size = $ - .local_vars
584
.local_vars_size = $ - .local_vars
578
.saved_regs     rd      4
585
.saved_regs     rd      4
579
.buffer         dd      ?
586
.buffer         dd      ?
580
end virtual
587
end virtual
581
; 4. Scan for the requested sector in the cache.
588
; 4. Scan for the requested sector in the cache.
582
; If found, copy the data and return.
589
; If found, copy the data and return.
583
; 4a. Acquire the lock.
590
; 4a. Acquire the lock.
584
        mov     ecx, [ebp+PARTITION.Disk]
591
        mov     ecx, [ebp+PARTITION.Disk]
585
        add     ecx, DISK.CacheLock
592
        add     ecx, DISK.CacheLock
586
        call    mutex_lock
593
        call    mutex_lock
587
; 4b. Call the lookup function without adding to the cache.
594
; 4b. Call the lookup function without adding to the cache.
588
        mov     eax, [.sector_lo]
595
        mov     eax, [.sector_lo]
589
        mov     edx, [.sector_hi]
596
        mov     edx, [.sector_hi]
590
        call    cache_lookup_read
597
        call    cache_lookup_read
591
; If not found, go to 5.
598
; If not found, go to 5.
592
        jc      .not_found_in_cache
599
        jc      .not_found_in_cache
593
.found_in_cache:
600
.found_in_cache:
594
; 4c. Copy the data.
601
; 4c. Copy the data.
-
 
602
        mov     esi, edi
595
        mov     edi, [.buffer]
603
        mov     edi, [.buffer]
596
        mov     esi, ecx
604
        mov     eax, 1
597
        shl     esi, 9
605
        shl     eax, cl
598
        add     esi, [ebx+DISKCACHE.data]
606
        mov     ecx, eax
599
        mov     ecx, 512/4
607
        shr     ecx, 2
600
        rep movsd
608
        rep movsd
601
; 4d. Release the lock and return success.
609
; 4d. Release the lock and return success.
602
        mov     ecx, [ebp+PARTITION.Disk]
610
        mov     ecx, [ebp+PARTITION.Disk]
603
        add     ecx, DISK.CacheLock
611
        add     ecx, DISK.CacheLock
604
        call    mutex_unlock
612
        call    mutex_unlock
605
.return:
613
.return:
606
        xor     eax, eax
614
        xor     eax, eax
607
.return_eax:
615
.return_eax:
608
        add     esp, .local_vars_size
616
        add     esp, .local_vars_size
609
        pop     edi esi edx ecx
617
        pop     edi esi edx ecx
610
        pop     ebx
618
        pop     ebx
611
        ret
619
        ret
612
.not_found_in_cache:
620
.not_found_in_cache:
613
; 5. Decide whether we need to prefetch further sectors.
621
; 5. Decide whether we need to prefetch further sectors.
614
; If so, advance to 6. If not, go to 13.
622
; If so, advance to 6. If not, go to 13.
615
; Assume that devices < 3MB are floppies which are slow
623
; Assume that devices < 3MB are floppies which are slow
616
; (ramdisk does not have a cache, so we don't even get here for ramdisk).
624
; (ramdisk does not have a cache, so we don't even get here for ramdisk).
617
; This is a dirty hack, but the entire function is somewhat hacky. Use fs_read64*.
625
; This is a dirty hack, but the entire function is somewhat hacky. Use fs_read64*.
618
        mov     eax, [ebp+PARTITION.Disk]
626
        mov     eax, [ebp+PARTITION.Disk]
619
        cmp     dword [eax+DISK.MediaInfo.Capacity+4], 0
627
        cmp     dword [eax+DISK.MediaInfo.Capacity+4], 0
620
        jnz     @f
628
        jnz     @f
621
        cmp     dword [eax+DISK.MediaInfo.Capacity], 3 shl (20-9)
629
        cmp     dword [eax+DISK.MediaInfo.Capacity], 3 shl (20-9)
622
        jb      .floppy
630
        jb      .floppy
623
@@:
631
@@:
624
; We want to prefetch CACHE_LEGACY_READ_SIZE sectors.
632
; We want to prefetch CACHE_LEGACY_READ_SIZE sectors.
625
; 6. Release the lock acquired at step 4a.
633
; 6. Release the lock acquired at step 4a.
626
        mov     ecx, [ebp+PARTITION.Disk]
634
        mov     ecx, [ebp+PARTITION.Disk]
627
        add     ecx, DISK.CacheLock
635
        add     ecx, DISK.CacheLock
628
        call    mutex_unlock
636
        call    mutex_unlock
629
; 7. Allocate buffer for CACHE_LEGACY_READ_SIZE sectors.
637
; 7. Allocate buffer for CACHE_LEGACY_READ_SIZE sectors.
630
        stdcall kernel_alloc, CACHE_LEGACY_READ_SIZE shl 9
638
        mov     eax, CACHE_LEGACY_READ_SIZE
-
 
639
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
-
 
640
        shl     eax, cl
-
 
641
        stdcall kernel_alloc, eax
631
; If failed, return the corresponding error code.
642
; If failed, return the corresponding error code.
632
        test    eax, eax
643
        test    eax, eax
633
        jz      .nomemory
644
        jz      .nomemory
634
; 8. Create second portion of local variables.
645
; 8. Create second portion of local variables.
635
        push    eax eax
646
        push    eax eax
636
        push    CACHE_LEGACY_READ_SIZE
647
        push    CACHE_LEGACY_READ_SIZE
637
virtual at esp
648
virtual at esp
638
.local_vars2:
649
.local_vars2:
639
.num_sectors            dd      ?       ; number of sectors left
650
.num_sectors            dd      ?       ; number of sectors left
640
.current_buffer         dd      ?       ; pointer to data that are currently copying
651
.current_buffer         dd      ?       ; pointer to data that are currently copying
641
.allocated_buffer       dd      ?       ; saved at safe place
652
.allocated_buffer       dd      ?       ; saved at safe place
642
.local_vars2_size = $ - .local_vars2
653
.local_vars2_size = $ - .local_vars2
643
end virtual
654
end virtual
644
; 9. Call the driver to read CACHE_LEGACY_READ_SIZE sectors.
655
; 9. Call the driver to read CACHE_LEGACY_READ_SIZE sectors.
645
        push    esp     ; numsectors
656
        push    esp     ; numsectors
646
        push    [.sector_hi+.local_vars2_size+4]        ; startsector
657
        push    [.sector_hi+.local_vars2_size+4]        ; startsector
647
        push    [.sector_lo+.local_vars2_size+8]        ; startsector
658
        push    [.sector_lo+.local_vars2_size+8]        ; startsector
648
        push    eax     ; buffer
659
        push    eax     ; buffer
649
        mov     esi, [ebp+PARTITION.Disk]
660
        mov     esi, [ebp+PARTITION.Disk]
650
        mov     al, DISKFUNC.read
661
        mov     al, DISKFUNC.read
651
        call    disk_call_driver
662
        call    disk_call_driver
652
; Note: we're ok if at least one sector is read,
663
; Note: we're ok if at least one sector is read,
653
; read error somewhere after that just limits data to be put in cache.
664
; read error somewhere after that just limits data to be put in cache.
654
        cmp     [.num_sectors], 0
665
        cmp     [.num_sectors], 0
655
        jz      .read_error
666
        jz      .read_error
656
; 10. Copy data for the caller.
667
; 10. Copy data for the caller.
657
        mov     esi, [.allocated_buffer]
668
        mov     esi, [.allocated_buffer]
658
        mov     edi, [.buffer+.local_vars2_size]
669
        mov     edi, [.buffer+.local_vars2_size]
-
 
670
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
-
 
671
        mov     eax, 1
-
 
672
        shl     eax, cl
659
        mov     ecx, 512/4
673
        mov     ecx, eax
-
 
674
        shr     ecx, 2
660
        rep movsd
675
        rep movsd
661
; 11. Store all sectors that were successfully read to the cache.
676
; 11. Store all sectors that were successfully read to the cache.
662
; 11a. Acquire the lock.
677
; 11a. Acquire the lock.
663
        mov     ecx, [ebp+PARTITION.Disk]
678
        mov     ecx, [ebp+PARTITION.Disk]
664
        add     ecx, DISK.CacheLock
679
        add     ecx, DISK.CacheLock
665
        call    mutex_lock
680
        call    mutex_lock
666
.store_to_cache:
681
.store_to_cache:
667
; 11b. For each sector, call the lookup function with adding to the cache, if not yet.
682
; 11b. For each sector, call the lookup function with adding to the cache, if not yet.
668
        mov     eax, [.sector_lo+.local_vars2_size]
683
        mov     eax, [.sector_lo+.local_vars2_size]
669
        mov     edx, [.sector_hi+.local_vars2_size]
684
        mov     edx, [.sector_hi+.local_vars2_size]
670
        call    cache_lookup_write
685
        call    cache_lookup_write
671
        test    eax, eax
686
        test    eax, eax
672
        jnz     .cache_error
687
        jnz     .cache_error
673
; 11c. Ignore sectors marked as modified: for them the cache is more recent that disk data.
688
; 11c. Ignore sectors marked as modified: for them the cache is more recent that disk data.
-
 
689
        mov     eax, 1
-
 
690
        shl     eax, cl
674
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
691
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
675
        jnz     .not_modified
692
        jnz     .not_modified
676
        add     [.current_buffer], 512
693
        add     [.current_buffer], eax
677
        jmp     .sector_done
694
        jmp     .sector_done
678
.not_modified:
695
.not_modified:
679
; 11d. For each sector, copy data, mark the item as not-modified copy of the disk,
696
; 11d. For each sector, copy data, mark the item as not-modified copy of the disk,
680
; advance .current_buffer and .sector_hi:.sector_lo to the next sector.
697
; advance .current_buffer and .sector_hi:.sector_lo to the next sector.
681
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
698
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
682
        mov     esi, [.current_buffer]
699
        mov     esi, [.current_buffer]
683
        mov     edi, ecx
700
        mov     ecx, eax
684
        shl     edi, 9
701
        shr     ecx, 2
685
        add     edi, [ebx+DISKCACHE.data]
-
 
686
        mov     ecx, 512/4
-
 
687
        rep movsd
702
        rep movsd
688
        mov     [.current_buffer], esi
703
        mov     [.current_buffer], esi
689
.sector_done:
704
.sector_done:
690
        add     [.sector_lo+.local_vars2_size], 1
705
        add     [.sector_lo+.local_vars2_size], 1
691
        adc     [.sector_hi+.local_vars2_size], 0
706
        adc     [.sector_hi+.local_vars2_size], 0
692
; 11e. Continue the loop at 11b-11d until all sectors are processed.
707
; 11e. Continue the loop at 11b-11d until all sectors are processed.
693
        dec     [.num_sectors]
708
        dec     [.num_sectors]
694
        jnz     .store_to_cache
709
        jnz     .store_to_cache
695
.cache_error:
710
.cache_error:
696
; 11f. Release the lock.
711
; 11f. Release the lock.
697
        mov     ecx, [ebp+PARTITION.Disk]
712
        mov     ecx, [ebp+PARTITION.Disk]
698
        add     ecx, DISK.CacheLock
713
        add     ecx, DISK.CacheLock
699
        call    mutex_unlock
714
        call    mutex_unlock
700
.copy_done:
715
.copy_done:
701
; 12. Remove portion of local variables created at step 8,
716
; 12. Remove portion of local variables created at step 8,
702
; free the buffer allocated at step 7 and return.
717
; free the buffer allocated at step 7 and return.
703
        pop     ecx ecx
718
        pop     ecx ecx
704
        stdcall kernel_free
719
        stdcall kernel_free
705
        jmp     .return
720
        jmp     .return
706
.read_error:
721
.read_error:
707
; If no sectors were read, free the buffer allocated at step 7
722
; If no sectors were read, free the buffer allocated at step 7
708
; and pass the error to the caller.
723
; and pass the error to the caller.
709
        push    eax
724
        push    eax
710
        stdcall kernel_free, [.allocated_buffer+4]
725
        stdcall kernel_free, [.allocated_buffer+4]
711
        pop     eax
726
        pop     eax
712
        add     esp, .local_vars2_size
727
        add     esp, .local_vars2_size
713
        jmp     .return_eax
728
        jmp     .return_eax
714
.nomemory:
729
.nomemory:
715
        mov     eax, DISK_STATUS_NO_MEMORY
730
        mov     eax, DISK_STATUS_NO_MEMORY
716
        jmp     .return_eax
731
        jmp     .return_eax
717
.floppy:
732
.floppy:
718
; We don't want to prefetch anything, just read one sector.
733
; We don't want to prefetch anything, just read one sector.
719
; We are still holding the lock acquired at step 4a.
734
; We are still holding the lock acquired at step 4a.
720
; 13. Call the lookup function adding sector to the cache.
735
; 13. Call the lookup function adding sector to the cache.
721
        call    cache_lookup_write
736
        call    cache_lookup_write
722
        test    eax, eax
737
        test    eax, eax
723
        jnz     .floppy_cache_error
738
        jnz     .floppy_cache_error
724
        push    ecx
739
        push    esi
725
 
740
 
726
; 14. Call the driver to read one sector.
741
; 14. Call the driver to read one sector.
727
        push    1
742
        push    1
728
        push    esp
743
        push    esp
729
        push    edx
744
        push    edx
730
        push    [.sector_lo+16]
745
        push    [.sector_lo+16]
731
        shl     ecx, 9
-
 
732
        add     ecx, [ebx+DISKCACHE.data]
-
 
733
        push    ecx
746
        push    edi
734
        mov     esi, [ebp+PARTITION.Disk]
747
        mov     esi, [ebp+PARTITION.Disk]
735
        mov     al, DISKFUNC.read
748
        mov     al, DISKFUNC.read
736
        call    disk_call_driver
749
        call    disk_call_driver
737
        pop     ecx
750
        pop     ecx
738
        dec     ecx
751
        dec     ecx
739
        jnz     .floppy_read_error
752
        jnz     .floppy_read_error
740
; 15. Get the slot and pointer to the cache item,
753
; 15. Get the slot and pointer to the cache item,
741
; change the status to not-modified copy of the disk
754
; change the status to not-modified copy of the disk
742
; and go to 4c.
755
; and go to 4c.
743
        pop     ecx
756
        pop     esi
744
        lea     esi, [ecx*sizeof.CACHE_ITEM/4]
-
 
745
        shl     esi, 2
-
 
746
        add     esi, [ebx+DISKCACHE.pointer]
-
 
747
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
757
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
748
        jmp     .found_in_cache
758
        jmp     .found_in_cache
749
 
759
 
750
; On error at steps 13-14, release the lock
760
; On error at steps 13-14, release the lock
751
; and pass the error to the caller.
761
; and pass the error to the caller.
752
.floppy_read_error:
762
.floppy_read_error:
753
        pop     ecx
763
        pop     ecx
754
.floppy_cache_error:
764
.floppy_cache_error:
755
        mov     ecx, [ebp+PARTITION.Disk]
765
        mov     ecx, [ebp+PARTITION.Disk]
756
        add     ecx, DISK.CacheLock
766
        add     ecx, DISK.CacheLock
757
        push    eax
767
        push    eax
758
        call    mutex_unlock
768
        call    mutex_unlock
759
        pop     eax
769
        pop     eax
760
        jmp     .return_eax
770
        jmp     .return_eax
761
 
771
 
762
; This function is intended to replace the old 'hd_write' function when
772
; This function is intended to replace the old 'hd_write' function when
763
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
773
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
764
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
774
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
765
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
775
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
766
; eax is relative to partition start
776
; eax is relative to partition start
767
; out: eax = error code; 0 = ok
777
; out: eax = error code; 0 = ok
768
fs_write32_sys:
778
fs_write32_sys:
769
; Just call the advanced function.
779
; Just call the advanced function.
770
        push    ecx edx
780
        push    ecx edx
771
        xor     edx, edx
781
        xor     edx, edx
772
        mov     ecx, 1
782
        mov     ecx, 1
773
        call    fs_write64_sys
783
        call    fs_write64_sys
774
        pop     edx ecx
784
        pop     edx ecx
775
        ret
785
        ret
776
 
786
 
777
; This function is intended to replace the old 'hd_write' function when
787
; This function is intended to replace the old 'hd_write' function when
778
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
788
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
779
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
789
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
780
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
790
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
781
; eax is relative to partition start
791
; eax is relative to partition start
782
; out: eax = error code; 0 = ok
792
; out: eax = error code; 0 = ok
783
fs_write32_app:
793
fs_write32_app:
784
; Just call the advanced function.
794
; Just call the advanced function.
785
        push    ecx edx
795
        push    ecx edx
786
        xor     edx, edx
796
        xor     edx, edx
787
        mov     ecx, 1
797
        mov     ecx, 1
788
        call    fs_write64_app
798
        call    fs_write64_app
789
        pop     edx ecx
799
        pop     edx ecx
790
        ret
800
        ret
791
 
801
 
792
; Lookup for the given sector in the given cache.
802
; Lookup for the given sector in the given cache.
793
; If the sector is not present, return error.
803
; If the sector is not present, return error.
794
; The caller must acquire the cache lock.
804
; The caller must acquire the cache lock.
795
; in: edx:eax = sector
805
; in: edx:eax = sector
796
; in: ebx -> DISKCACHE structure
806
; in: ebx -> DISKCACHE structure
797
; out: CF set if sector is not in cache
807
; out: CF set if sector is not in cache
798
; out: ecx = index in cache
808
; out: ecx = sector_size_log
799
; out: esi -> sector:status
809
; out: esi -> sector:status
-
 
810
; out: edi -> sector data
800
proc cache_lookup_read
811
proc cache_lookup_read
801
        mov     esi, [ebx+DISKCACHE.pointer]
812
        mov     esi, [ebx+DISKCACHE.pointer]
802
        add     esi, sizeof.CACHE_ITEM
813
        add     esi, sizeof.CACHE_ITEM
803
 
814
 
804
        mov     ecx, 1
815
        mov     edi, 1
805
 
816
 
806
.hdreadcache:
817
.hdreadcache:
807
 
818
 
808
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
819
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
809
        je      .nohdcache
820
        je      .nohdcache
810
 
821
 
811
        cmp     [esi+CACHE_ITEM.SectorLo], eax
822
        cmp     [esi+CACHE_ITEM.SectorLo], eax
812
        jne     .nohdcache
823
        jne     .nohdcache
813
        cmp     [esi+CACHE_ITEM.SectorHi], edx
824
        cmp     [esi+CACHE_ITEM.SectorHi], edx
814
        jne     .nohdcache
825
        jne     .nohdcache
-
 
826
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
-
 
827
        shl     edi, cl
-
 
828
        add     edi, [ebx+DISKCACHE.data]
815
        clc
829
        clc
816
        ret
830
        ret
817
 
831
 
818
.nohdcache:
832
.nohdcache:
819
 
833
 
820
        add     esi, sizeof.CACHE_ITEM
834
        add     esi, sizeof.CACHE_ITEM
821
        inc     ecx
835
        inc     edi
822
        cmp     ecx, [ebx+DISKCACHE.sad_size]
836
        cmp     edi, [ebx+DISKCACHE.sad_size]
823
        jbe     .hdreadcache
837
        jbe     .hdreadcache
824
        stc
838
        stc
825
        ret
839
        ret
826
endp
840
endp
827
 
841
 
828
; Lookup for the given sector in the given cache.
842
; Lookup for the given sector in the given cache.
829
; If the sector is not present, allocate space for it,
843
; If the sector is not present, allocate space for it,
830
; possibly flushing data.
844
; possibly flushing data.
831
; in: edx:eax = sector
845
; in: edx:eax = sector
832
; in: ebx -> DISKCACHE structure
846
; in: ebx -> DISKCACHE structure
833
; in: ebp -> PARTITION structure
847
; in: ebp -> PARTITION structure
834
; out: eax = error code
848
; out: eax = error code
835
; out: ecx = index in cache
-
 
836
; out: esi -> sector:status
849
; out: esi -> sector:status
-
 
850
; out: edi -> sector data
837
proc cache_lookup_write
851
proc cache_lookup_write
838
        call    cache_lookup_read
852
        call    cache_lookup_read
839
        jnc     .return0
853
        jnc     .return0
840
        push    edx eax
854
        push    edx eax
841
;-----------------------------------------------------------
855
;-----------------------------------------------------------
842
; find empty or read slot, flush cache if next 12.5% is used by write
856
; find empty or read slot, flush cache if next 12.5% is used by write
843
; output : ecx = cache slot
857
; output : ecx = cache slot
844
;-----------------------------------------------------------
858
;-----------------------------------------------------------
845
; Note: the code is essentially inherited, so probably
859
; Note: the code is essentially inherited, so probably
846
; no analysis of efficiency were done.
860
; no analysis of efficiency were done.
847
; However, it works.
861
; However, it works.
848
.search_again:
862
.search_again:
849
        mov     eax, [ebx+DISKCACHE.sad_size]
863
        mov     eax, [ebx+DISKCACHE.sad_size]
850
        mov     ecx, [ebx+DISKCACHE.search_start]
864
        mov     ecx, [ebx+DISKCACHE.search_start]
851
        shr     eax, 3
865
        shr     eax, 3
852
        lea     esi, [ecx*sizeof.CACHE_ITEM/4]
866
        lea     esi, [ecx*sizeof.CACHE_ITEM/4]
853
        shl     esi, 2
867
        shl     esi, 2
854
        add     esi, [ebx+DISKCACHE.pointer]
868
        add     esi, [ebx+DISKCACHE.pointer]
855
.search_for_empty:
869
.search_for_empty:
856
        inc     ecx
870
        inc     ecx
857
        add     esi, sizeof.CACHE_ITEM
871
        add     esi, sizeof.CACHE_ITEM
858
        cmp     ecx, [ebx+DISKCACHE.sad_size]
872
        cmp     ecx, [ebx+DISKCACHE.sad_size]
859
        jbe     .inside_cache
873
        jbe     .inside_cache
860
        mov     ecx, 1
874
        mov     ecx, 1
861
        mov     esi, [ebx+DISKCACHE.pointer]
875
        mov     esi, [ebx+DISKCACHE.pointer]
862
        add     esi, sizeof.CACHE_ITEM
876
        add     esi, sizeof.CACHE_ITEM
863
.inside_cache:
877
.inside_cache:
864
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
878
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
865
        jb      .found_slot             ; it's empty or read
879
        jb      .found_slot             ; it's empty or read
866
        dec     eax
880
        dec     eax
867
        jnz     .search_for_empty
881
        jnz     .search_for_empty
868
        stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all
882
        stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all
869
        test    eax, eax
883
        test    eax, eax
870
        jne     .found_slot_access_denied
884
        jne     .found_slot_access_denied
871
        jmp     .search_again           ; and start again
885
        jmp     .search_again           ; and start again
872
.found_slot:
886
.found_slot:
873
        mov     [ebx+DISKCACHE.search_start], ecx
887
        mov     [ebx+DISKCACHE.search_start], ecx
874
        popd    [esi+CACHE_ITEM.SectorLo]
888
        popd    [esi+CACHE_ITEM.SectorLo]
875
        popd    [esi+CACHE_ITEM.SectorHi]
889
        popd    [esi+CACHE_ITEM.SectorHi]
876
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
890
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
-
 
891
        mov     edi, ecx
-
 
892
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
-
 
893
        shl     edi, cl
-
 
894
        add     edi, [ebx+DISKCACHE.data]
877
.return0:
895
.return0:
878
        xor     eax, eax        ; success
896
        xor     eax, eax        ; success
879
        ret
897
        ret
880
.found_slot_access_denied:
898
.found_slot_access_denied:
881
        add     esp, 8
899
        add     esp, 8
882
        ret
900
        ret
883
endp
901
endp
884
 
902
 
885
; Flush the given cache.
903
; Flush the given cache.
886
; The caller must acquire the cache lock.
904
; The caller must acquire the cache lock.
887
; in: ebx -> DISKCACHE
905
; in: ebx -> DISKCACHE
888
; in: first argument in stdcall convention -> PARTITION
906
; in: first argument in stdcall convention -> PARTITION
889
proc write_cache64
907
proc write_cache64
890
; 1. Setup stack frame.
908
; 1. Setup stack frame.
891
        push    esi edi         ; save used registers to be stdcall
909
        push    esi edi         ; save used registers to be stdcall
892
        sub     esp, .local_vars_size   ; reserve space for local vars
910
        sub     esp, .local_vars_size   ; reserve space for local vars
893
virtual at esp
911
virtual at esp
894
.local_vars:
912
.local_vars:
895
.cache_end      dd      ?       ; item past the end of the cache
913
.cache_end      dd      ?       ; item past the end of the cache
896
.size_left      dd      ?       ; items left to scan
914
.size_left      dd      ?       ; items left to scan
897
.current_ptr    dd      ?       ; pointer to the current item
915
.current_ptr    dd      ?       ; pointer to the current item
898
;
916
;
899
; Write operations are coalesced in chains,
917
; Write operations are coalesced in chains,
900
; one chain describes a sequential interval of sectors,
918
; one chain describes a sequential interval of sectors,
901
; they can be sequential or scattered in the cache.
919
; they can be sequential or scattered in the cache.
902
.sequential     dd      ?
920
.sequential     dd      ?
903
; boolean variable, 1 if the current chain is sequential in the cache,
921
; boolean variable, 1 if the current chain is sequential in the cache,
904
; 0 if additional buffer is needed to perform the operation
922
; 0 if additional buffer is needed to perform the operation
905
.chain_start_pos dd     ?       ; slot of chain start item
923
.chain_start_pos dd     ?       ; data of chain start item
906
.chain_start_ptr dd     ?       ; pointer to chain start item
924
.chain_start_ptr dd     ?       ; pointer to chain start item
907
.chain_size     dd      ?       ; chain size (thanks, C.O.)
925
.chain_size     dd      ?       ; chain size (thanks, C.O.)
908
.iteration_size dd      ?
926
.iteration_size dd      ?
909
; If the chain size is too large, split the operation to several iterations.
927
; If the chain size is too large, split the operation to several iterations.
910
; This is size in sectors for one iterations.
928
; This is size in sectors for one iterations.
911
.iteration_buffer dd    ?       ; temporary buffer for non-sequential chains
929
.iteration_buffer dd    ?       ; temporary buffer for non-sequential chains
912
.local_vars_size = $ - .local_vars
930
.local_vars_size = $ - .local_vars
913
                rd      2       ; saved registers
931
                rd      2       ; saved registers
914
                dd      ?       ; return address
932
                dd      ?       ; return address
915
.disk           dd      ?       ; first argument
933
.disk           dd      ?       ; first argument
916
end virtual
934
end virtual
917
; 1. If there is no cache for this disk, nothing to do, just return zero.
935
; 1. If there is no cache for this disk, nothing to do, just return zero.
918
        cmp     [ebx+DISKCACHE.pointer], 0
936
        cmp     [ebx+DISKCACHE.pointer], 0
919
        jz      .return0
937
        jz      .return0
920
; 2. Prepare for the loop: initialize current pointer and .size_left,
938
; 2. Prepare for the loop: initialize current pointer and .size_left,
921
; calculate .cache_end.
939
; calculate .cache_end.
922
        mov     ecx, [ebx+DISKCACHE.sad_size]
940
        mov     ecx, [ebx+DISKCACHE.sad_size]
923
        mov     [.size_left], ecx
941
        mov     [.size_left], ecx
924
        lea     ecx, [ecx*sizeof.CACHE_ITEM/4]
942
        lea     ecx, [ecx*sizeof.CACHE_ITEM/4]
925
        shl     ecx, 2
943
        shl     ecx, 2
926
        mov     esi, [ebx+DISKCACHE.pointer]
944
        mov     esi, [ebx+DISKCACHE.pointer]
927
        add     esi, sizeof.CACHE_ITEM
945
        add     esi, sizeof.CACHE_ITEM
928
        add     ecx, esi
946
        add     ecx, esi
929
        mov     [.cache_end], ecx
947
        mov     [.cache_end], ecx
930
; 3. Main loop: go over all items, go to 5 for every modified item.
948
; 3. Main loop: go over all items, go to 5 for every modified item.
931
.look:
949
.look:
932
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
950
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
933
        jz      .begin_write
951
        jz      .begin_write
934
.look_next:
952
.look_next:
935
        add     esi, sizeof.CACHE_ITEM
953
        add     esi, sizeof.CACHE_ITEM
936
        dec     [.size_left]
954
        dec     [.size_left]
937
        jnz     .look
955
        jnz     .look
938
; 4. Return success.
956
; 4. Return success.
939
.return0:
957
.return0:
940
        xor     eax, eax
958
        xor     eax, eax
941
.return:
959
.return:
942
        add     esp, .local_vars_size
960
        add     esp, .local_vars_size
943
        pop     edi esi         ; restore used registers to be stdcall
961
        pop     edi esi         ; restore used registers to be stdcall
944
        ret     4               ; return popping one argument
962
        ret     4               ; return popping one argument
945
.begin_write:
963
.begin_write:
946
; We have found a modified item.
964
; We have found a modified item.
947
; 5. Prepare for chain finding: save the current item, initialize chain variables.
965
; 5. Prepare for chain finding: save the current item, initialize chain variables.
948
        mov     [.current_ptr], esi
966
        mov     [.current_ptr], esi
949
; Initialize chain as sequential zero-length starting at the current item.
967
; Initialize chain as sequential zero-length starting at the current item.
950
        mov     [.chain_start_ptr], esi
968
        mov     [.chain_start_ptr], esi
951
        mov     eax, [ebx+DISKCACHE.sad_size]
969
        mov     eax, [ebx+DISKCACHE.sad_size]
952
        sub     eax, [.size_left]
970
        sub     eax, [.size_left]
953
        inc     eax
971
        inc     eax
-
 
972
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
-
 
973
        shl     eax, cl
-
 
974
        add     eax, [ebx+DISKCACHE.data]
954
        mov     [.chain_start_pos], eax
975
        mov     [.chain_start_pos], eax
955
        mov     [.chain_size], 0
976
        mov     [.chain_size], 0
956
        mov     [.sequential], 1
977
        mov     [.sequential], 1
957
; 6. Expand the chain backward.
978
; 6. Expand the chain backward.
958
; Note: the main loop in step 2 looks for items sequentially,
979
; Note: the main loop in step 2 looks for items sequentially,
959
; so the previous item is not modified. If the previous sector
980
; so the previous item is not modified. If the previous sector
960
; is present in the cache, it automatically makes the chain scattered.
981
; is present in the cache, it automatically makes the chain scattered.
961
; 6a. Calculate sector number: one before the sector for the current item.
982
; 6a. Calculate sector number: one before the sector for the current item.
962
        mov     eax, [esi+CACHE_ITEM.SectorLo]
983
        mov     eax, [esi+CACHE_ITEM.SectorLo]
963
        mov     edx, [esi+CACHE_ITEM.SectorHi]
984
        mov     edx, [esi+CACHE_ITEM.SectorHi]
964
        sub     eax, 1
985
        sub     eax, 1
965
        sbb     edx, 0
986
        sbb     edx, 0
966
.find_chain_start:
987
.find_chain_start:
967
; 6b. For each sector where the previous item does not expand the chain,
988
; 6b. For each sector where the previous item does not expand the chain,
968
; call the lookup function without adding to the cache.
989
; call the lookup function without adding to the cache.
969
        call    cache_lookup_read
990
        call    cache_lookup_read
970
; 6c. If the sector is not found in cache or is not modified, stop expanding
991
; 6c. If the sector is not found in cache or is not modified, stop expanding
971
; and advance to step 7.
992
; and advance to step 7.
972
        jc      .found_chain_start
993
        jc      .found_chain_start
973
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
994
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
974
        jnz     .found_chain_start
995
        jnz     .found_chain_start
975
; 6d. We have found a new block that expands the chain backwards.
996
; 6d. We have found a new block that expands the chain backwards.
976
; It makes the chain non-sequential.
997
; It makes the chain non-sequential.
977
; Normally, sectors come in sequential blocks, so try to look at previous items
998
; Normally, sectors come in sequential blocks, so try to look at previous items
978
; before returning to 6b; if there is a sequential block indeed, this saves some
999
; before returning to 6b; if there is a sequential block indeed, this saves some
979
; time instead of many full-fledged lookups.
1000
; time instead of many full-fledged lookups.
980
        mov     [.sequential], 0
1001
        mov     [.sequential], 0
981
        mov     [.chain_start_pos], ecx
1002
        mov     [.chain_start_pos], edi
982
.look_backward:
1003
.look_backward:
983
; 6e. For each sector, update chain start pos/ptr, decrement sector number,
1004
; 6e. For each sector, update chain start pos/ptr, decrement sector number,
984
; look at the previous item.
1005
; look at the previous item.
985
        mov     [.chain_start_ptr], esi
1006
        mov     [.chain_start_ptr], esi
986
        inc     [.chain_size]
1007
        inc     [.chain_size]
987
        sub     eax, 1
1008
        sub     eax, 1
988
        sbb     edx, 0
1009
        sbb     edx, 0
989
        sub     esi, sizeof.CACHE_ITEM
1010
        sub     esi, sizeof.CACHE_ITEM
990
; If the previous item exists...
1011
; If the previous item exists...
991
        cmp     esi, [ebx+DISKCACHE.pointer]
1012
        cmp     esi, [ebx+DISKCACHE.pointer]
992
        jbe     .find_chain_start
1013
        jbe     .find_chain_start
993
; ...describes the correct sector...
1014
; ...describes the correct sector...
994
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1015
        cmp     [esi+CACHE_ITEM.SectorLo], eax
995
        jnz     .find_chain_start
1016
        jnz     .find_chain_start
996
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1017
        cmp     [esi+CACHE_ITEM.SectorHi], edx
997
        jnz     .find_chain_start
1018
        jnz     .find_chain_start
998
; ...and is modified...
1019
; ...and is modified...
999
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1020
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1000
        jnz     .found_chain_start
1021
        jnz     .found_chain_start
1001
; ...expand the chain one sector backwards and continue the loop at 6e.
1022
; ...expand the chain one sector backwards and continue the loop at 6e.
1002
; Otherwise, advance to step 7 if the previous item describes the correct sector
1023
; Otherwise, advance to step 7 if the previous item describes the correct sector
1003
; but is not modified, and return to step 6b otherwise.
1024
; but is not modified, and return to step 6b otherwise.
-
 
1025
        mov     edi, 1
-
 
1026
        shl     edi, cl
1004
        dec     [.chain_start_pos]
1027
        sub     [.chain_start_pos], edi
1005
        jmp     .look_backward
1028
        jmp     .look_backward
1006
.found_chain_start:
1029
.found_chain_start:
1007
; 7. Expand the chain forward.
1030
; 7. Expand the chain forward.
1008
; 7a. Prepare for the loop at 7b:
1031
; 7a. Prepare for the loop at 7b:
1009
; set esi = pointer to current item, edx:eax = current sector.
1032
; set esi = pointer to current item, edx:eax = current sector.
1010
        mov     esi, [.current_ptr]
1033
        mov     esi, [.current_ptr]
1011
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1034
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1012
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1035
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1013
.look_forward:
1036
.look_forward:
1014
; 7b. First, look at the next item. If it describes the next sector:
1037
; 7b. First, look at the next item. If it describes the next sector:
1015
; if it is modified, expand the chain with that sector and continue this step,
1038
; if it is modified, expand the chain with that sector and continue this step,
1016
; if it is not modified, the chain is completed, so advance to step 8.
1039
; if it is not modified, the chain is completed, so advance to step 8.
1017
        inc     [.chain_size]
1040
        inc     [.chain_size]
1018
        add     eax, 1
1041
        add     eax, 1
1019
        adc     edx, 0
1042
        adc     edx, 0
1020
        add     esi, sizeof.CACHE_ITEM
1043
        add     esi, sizeof.CACHE_ITEM
1021
        cmp     esi, [.cache_end]
1044
        cmp     esi, [.cache_end]
1022
        jae     .find_chain_end
1045
        jae     .find_chain_end
1023
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1046
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1024
        jnz     .find_chain_end
1047
        jnz     .find_chain_end
1025
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1048
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1026
        jnz     .find_chain_end
1049
        jnz     .find_chain_end
1027
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1050
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1028
        jnz     .found_chain_end
1051
        jnz     .found_chain_end
1029
        jmp     .look_forward
1052
        jmp     .look_forward
1030
.find_chain_end:
1053
.find_chain_end:
1031
; 7c. Otherwise, call the lookup function.
1054
; 7c. Otherwise, call the lookup function.
1032
        call    cache_lookup_read
1055
        call    cache_lookup_read
1033
; 7d. If the next sector is present in the cache and is modified,
1056
; 7d. If the next sector is present in the cache and is modified,
1034
; mark the chain as non-sequential and continue to step 7b.
1057
; mark the chain as non-sequential and continue to step 7b.
1035
        jc      .found_chain_end
1058
        jc      .found_chain_end
1036
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1059
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1037
        jnz     .found_chain_end
1060
        jnz     .found_chain_end
1038
        mov     [.sequential], 0
1061
        mov     [.sequential], 0
1039
        jmp     .look_forward
1062
        jmp     .look_forward
1040
.found_chain_end:
1063
.found_chain_end:
1041
; 8. Decide whether the chain is sequential or scattered.
1064
; 8. Decide whether the chain is sequential or scattered.
1042
; Advance to step 9 for sequential chains, go to step 10 for scattered chains.
1065
; Advance to step 9 for sequential chains, go to step 10 for scattered chains.
1043
        cmp     [.sequential], 0
1066
        cmp     [.sequential], 0
1044
        jz      .write_non_sequential
1067
        jz      .write_non_sequential
1045
.write_sequential:
1068
.write_sequential:
1046
; 9. Write a sequential chain to disk.
1069
; 9. Write a sequential chain to disk.
1047
; 9a. Pass the entire chain to the driver.
1070
; 9a. Pass the entire chain to the driver.
1048
        mov     eax, [.chain_start_ptr]
1071
        mov     eax, [.chain_start_ptr]
1049
        mov     edx, [.chain_start_pos]
-
 
1050
        shl     edx, 9
-
 
1051
        add     edx, [ebx+DISKCACHE.data]
-
 
1052
        lea     ecx, [.chain_size]
1072
        lea     ecx, [.chain_size]
1053
        push    ecx     ; numsectors
1073
        push    ecx     ; numsectors
1054
        pushd   [eax+CACHE_ITEM.SectorHi]       ; startsector
1074
        pushd   [eax+CACHE_ITEM.SectorHi]       ; startsector
1055
        pushd   [eax+CACHE_ITEM.SectorLo]       ; startsector
1075
        pushd   [eax+CACHE_ITEM.SectorLo]       ; startsector
1056
        push    edx     ; buffer
1076
        push    [.chain_start_pos+12]           ; buffer
1057
        mov     esi, [ebp+PARTITION.Disk]
1077
        mov     esi, [ebp+PARTITION.Disk]
1058
        mov     al, DISKFUNC.write
1078
        mov     al, DISKFUNC.write
1059
        call    disk_call_driver
1079
        call    disk_call_driver
1060
; 9b. If failed, pass the error code to the driver.
1080
; 9b. If failed, pass the error code to the driver.
1061
        test    eax, eax
1081
        test    eax, eax
1062
        jnz     .return
1082
        jnz     .return
1063
; 9c. If succeeded, mark all sectors in the chain as not-modified,
1083
; 9c. If succeeded, mark all sectors in the chain as not-modified,
1064
; advance current item and number of items left to skip the chain.
1084
; advance current item and number of items left to skip the chain.
1065
        mov     esi, [.current_ptr]
1085
        mov     esi, [.current_ptr]
1066
        mov     eax, [.chain_size]
1086
        mov     eax, [.chain_size]
1067
        sub     [.size_left], eax
1087
        sub     [.size_left], eax
1068
@@:
1088
@@:
1069
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1089
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1070
        add     esi, sizeof.CACHE_ITEM
1090
        add     esi, sizeof.CACHE_ITEM
1071
        dec     eax
1091
        dec     eax
1072
        jnz     @b
1092
        jnz     @b
1073
; 9d. Continue the main loop at step 2 if there are more sectors.
1093
; 9d. Continue the main loop at step 2 if there are more sectors.
1074
; Return success otherwise.
1094
; Return success otherwise.
1075
        cmp     [.size_left], 0
1095
        cmp     [.size_left], 0
1076
        jnz     .look
1096
        jnz     .look
1077
        jmp     .return0
1097
        jmp     .return0
1078
.write_non_sequential:
1098
.write_non_sequential:
1079
; Write a non-sequential chain to the disk.
1099
; Write a non-sequential chain to the disk.
1080
; 10. Allocate a temporary buffer.
1100
; 10. Allocate a temporary buffer.
1081
; Use [.chain_size] sectors, but
1101
; Use [.chain_size] sectors, but
1082
; not greater than CACHE_MAX_ALLOC_SIZE bytes
1102
; not greater than CACHE_MAX_ALLOC_SIZE bytes
1083
; and not greater than half of free memory.
1103
; and not greater than half of free memory.
1084
        mov     eax, [pg_data.pages_free]
1104
        mov     eax, [pg_data.pages_free]
1085
        shr     eax, 1
1105
        shr     eax, 1
1086
        jz      .nomemory
1106
        jz      .nomemory
1087
        cmp     eax, CACHE_MAX_ALLOC_SIZE shr 12
1107
        cmp     eax, CACHE_MAX_ALLOC_SIZE shr 12
1088
        jbe     @f
1108
        jbe     @f
1089
        mov     eax, CACHE_MAX_ALLOC_SIZE shr 12
1109
        mov     eax, CACHE_MAX_ALLOC_SIZE shr 12
1090
@@:
1110
@@:
1091
        shl     eax, 12 - 9
1111
        shl     eax, 12
-
 
1112
        shr     eax, cl
-
 
1113
        jz      .nomemory
1092
        cmp     eax, [.chain_size]
1114
        cmp     eax, [.chain_size]
1093
        jbe     @f
1115
        jbe     @f
1094
        mov     eax, [.chain_size]
1116
        mov     eax, [.chain_size]
1095
@@:
1117
@@:
1096
        mov     [.iteration_size], eax
1118
        mov     [.iteration_size], eax
1097
        shl     eax, 9
1119
        shl     eax, cl
1098
        stdcall kernel_alloc, eax
1120
        stdcall kernel_alloc, eax
1099
        test    eax, eax
1121
        test    eax, eax
1100
        jz      .nomemory
1122
        jz      .nomemory
1101
        mov     [.iteration_buffer], eax
1123
        mov     [.iteration_buffer], eax
1102
.write_non_sequential_iteration:
1124
.write_non_sequential_iteration:
1103
; 11. Split the chain so that each iteration fits in the allocated buffer.
1125
; 11. Split the chain so that each iteration fits in the allocated buffer.
1104
; Iteration size is the minimum of chain size and allocated size.
1126
; Iteration size is the minimum of chain size and allocated size.
1105
        mov     eax, [.chain_size]
1127
        mov     eax, [.chain_size]
1106
        cmp     eax, [.iteration_size]
1128
        cmp     eax, [.iteration_size]
1107
        jae     @f
1129
        jae     @f
1108
        mov     [.iteration_size], eax
1130
        mov     [.iteration_size], eax
1109
@@:
1131
@@:
1110
; 12. Prepare arguments for the driver.
1132
; 12. Prepare arguments for the driver.
1111
        mov     esi, [.chain_start_ptr]
1133
        mov     esi, [.chain_start_ptr]
1112
        mov     edi, [.iteration_buffer]
1134
        mov     edi, [.iteration_buffer]
1113
        push    [.iteration_size]
1135
        push    [.iteration_size]
1114
        push    esp     ; numsectors
1136
        push    esp     ; numsectors
1115
        push    [esi+CACHE_ITEM.SectorHi]       ; startsector
1137
        push    [esi+CACHE_ITEM.SectorHi]       ; startsector
1116
        push    [esi+CACHE_ITEM.SectorLo]       ; startsector
1138
        push    [esi+CACHE_ITEM.SectorLo]       ; startsector
1117
        push    edi     ; buffer
1139
        push    edi     ; buffer
1118
; 13. Copy data from the cache to the temporary buffer,
1140
; 13. Copy data from the cache to the temporary buffer,
1119
; advancing chain_start pos/ptr and marking sectors as not-modified.
1141
; advancing chain_start pos/ptr and marking sectors as not-modified.
1120
; 13a. Prepare for the loop: push number of sectors to process.
1142
; 13a. Prepare for the loop: push number of sectors to process.
1121
        push    [.iteration_size+20]    ; temporary variable
1143
        push    [.iteration_size+20]    ; temporary variable
1122
.copy_loop:
1144
.copy_loop:
1123
; 13b. For each sector, copy the data.
1145
; 13b. For each sector, copy the data.
1124
; Note that edi is advanced automatically.
1146
; Note that edi is advanced automatically.
1125
        mov     esi, [.chain_start_pos+24]
1147
        mov     esi, [.chain_start_pos+24]
-
 
1148
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
-
 
1149
        mov     eax, 1
1126
        shl     esi, 9
1150
        shl     eax, cl
1127
        add     esi, [ebx+DISKCACHE.data]
1151
        mov     ecx, eax
1128
        mov     ecx, 512/4
1152
        shr     ecx, 2
1129
        rep movsd
1153
        rep movsd
-
 
1154
        mov     ecx, eax ; keep for 13e
1130
; 13c. Mark the item as not-modified.
1155
; 13c. Mark the item as not-modified.
1131
        mov     esi, [.chain_start_ptr+24]
1156
        mov     esi, [.chain_start_ptr+24]
1132
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1157
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1133
; 13d. Check whether the next sector continues the chain.
1158
; 13d. Check whether the next sector continues the chain.
1134
; If so, advance to 13e. Otherwise, go to 13f.
1159
; If so, advance to 13e. Otherwise, go to 13f.
1135
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1160
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1136
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1161
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1137
        add     esi, sizeof.CACHE_ITEM
1162
        add     esi, sizeof.CACHE_ITEM
1138
        add     eax, 1
1163
        add     eax, 1
1139
        adc     edx, 0
1164
        adc     edx, 0
1140
        cmp     esi, [.cache_end+24]
1165
        cmp     esi, [.cache_end+24]
1141
        jae     .no_forward
1166
        jae     .no_forward
1142
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1167
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1143
        jnz     .no_forward
1168
        jnz     .no_forward
1144
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1169
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1145
        jnz     .no_forward
1170
        jnz     .no_forward
1146
; 13e. Increment position/pointer to the chain and
1171
; 13e. Increment position/pointer to the chain and
1147
; continue the loop.
1172
; continue the loop.
1148
        inc     [.chain_start_pos+24]
1173
        add     [.chain_start_pos+24], ecx
1149
        mov     [.chain_start_ptr+24], esi
1174
        mov     [.chain_start_ptr+24], esi
1150
        dec     dword [esp]
1175
        dec     dword [esp]
1151
        jnz     .copy_loop
1176
        jnz     .copy_loop
1152
        jmp     .copy_done
1177
        jmp     .copy_done
1153
.no_forward:
1178
.no_forward:
1154
; 13f. Call the lookup function without adding to the cache.
1179
; 13f. Call the lookup function without adding to the cache.
1155
; Update position/pointer with returned value.
1180
; Update position/pointer with returned value.
1156
; Note: for the last sector in the chain, ecx/esi may contain
1181
; Note: for the last sector in the chain, edi/esi may contain
1157
; garbage; we are not going to use them in this case.
1182
; garbage; we are not going to use them in this case.
-
 
1183
        push    edi
1158
        call    cache_lookup_read
1184
        call    cache_lookup_read
1159
        mov     [.chain_start_pos+24], ecx
1185
        mov     [.chain_start_pos+28], edi
1160
        mov     [.chain_start_ptr+24], esi
1186
        mov     [.chain_start_ptr+28], esi
-
 
1187
        pop     edi
1161
        dec     dword [esp]
1188
        dec     dword [esp]
1162
        jnz     .copy_loop
1189
        jnz     .copy_loop
1163
.copy_done:
1190
.copy_done:
1164
; 13g. Restore the stack after 13a.
1191
; 13g. Restore the stack after 13a.
1165
        pop     ecx
1192
        pop     ecx
1166
; 14. Call the driver.
1193
; 14. Call the driver.
1167
        mov     esi, [ebp+PARTITION.Disk]
1194
        mov     esi, [ebp+PARTITION.Disk]
1168
        mov     al, DISKFUNC.write
1195
        mov     al, DISKFUNC.write
1169
        call    disk_call_driver
1196
        call    disk_call_driver
1170
        pop     ecx     ; numsectors
1197
        pop     ecx     ; numsectors
1171
; 15. If the driver has returned an error, free the buffer allocated at step 10
1198
; 15. If the driver has returned an error, free the buffer allocated at step 10
1172
; and pass the error to the caller.
1199
; and pass the error to the caller.
1173
; Otherwise, remove the processed part from the chain and continue iterations
1200
; Otherwise, remove the processed part from the chain and continue iterations
1174
; starting in step 11 if there are more data to process.
1201
; starting in step 11 if there are more data to process.
1175
        test    eax, eax
1202
        test    eax, eax
1176
        jnz     .nonsequential_error
1203
        jnz     .nonsequential_error
1177
        sub     [.chain_size], ecx
1204
        sub     [.chain_size], ecx
1178
        jnz     .write_non_sequential_iteration
1205
        jnz     .write_non_sequential_iteration
1179
; 16. The chain is written. Free the temporary buffer
1206
; 16. The chain is written. Free the temporary buffer
1180
; and continue the loop at step 2.
1207
; and continue the loop at step 2.
1181
        stdcall kernel_free, [.iteration_buffer]
1208
        stdcall kernel_free, [.iteration_buffer]
1182
        mov     esi, [.current_ptr]
1209
        mov     esi, [.current_ptr]
1183
        jmp     .look_next
1210
        jmp     .look_next
1184
.nonsequential_error:
1211
.nonsequential_error:
1185
        push    eax
1212
        push    eax
1186
        stdcall kernel_free, [.iteration_buffer+4]
1213
        stdcall kernel_free, [.iteration_buffer+4]
1187
        pop     eax
1214
        pop     eax
1188
        jmp     .return
1215
        jmp     .return
1189
.nomemory:
1216
.nomemory:
1190
        mov     eax, DISK_STATUS_NO_MEMORY
1217
        mov     eax, DISK_STATUS_NO_MEMORY
1191
        jmp     .return
1218
        jmp     .return
1192
endp
1219
endp
1193
 
1220
 
1194
; This internal function is called from disk_add to initialize the caching for
1221
; This internal function is called from disk_add to initialize the caching for
1195
; a new DISK.
1222
; a new DISK.
1196
; The algorithm is inherited from getcache.inc: take 1/32 part of the available
1223
; The algorithm is inherited from getcache.inc: take 1/32 part of the available
1197
; physical memory, round down to 8 pages, limit by 128K from below and by 1M
1224
; physical memory, round down to 8 pages, limit by 128K from below and by 1M
1198
; from above. Reserve 1/8 part of the cache for system data and 7/8 for app
1225
; from above. Reserve 1/8 part of the cache for system data and 7/8 for app
1199
; data.
1226
; data.
1200
; After the size is calculated, but before the cache is allocated, the device
1227
; After the size is calculated, but before the cache is allocated, the device
1201
; driver can adjust the size. In particular, setting size to zero disables
1228
; driver can adjust the size. In particular, setting size to zero disables
1202
; caching: there is no sense in a cache for a ramdisk. In fact, such action
1229
; caching: there is no sense in a cache for a ramdisk. In fact, such action
1203
; is most useful example of a non-trivial adjustment.
1230
; is most useful example of a non-trivial adjustment.
1204
; esi = pointer to DISK structure
1231
; esi = pointer to DISK structure
1205
disk_init_cache:
1232
disk_init_cache:
-
 
1233
; 1. Verify sector size. The code requires it to be a power of 2 not less than 4.
-
 
1234
; In the name of sanity check that sector size is not too small or too large.
-
 
1235
        bsf     ecx, [esi+DISK.MediaInfo.SectorSize]
-
 
1236
        jz      .invalid_sector_size
-
 
1237
        mov     eax, 1
-
 
1238
        shl     eax, cl
-
 
1239
        cmp     eax, [esi+DISK.MediaInfo.SectorSize]
-
 
1240
        jnz     .invalid_sector_size
-
 
1241
        cmp     ecx, 6
-
 
1242
        jb      .invalid_sector_size
-
 
1243
        cmp     ecx, 14
-
 
1244
        jbe     .normal_sector_size
-
 
1245
.invalid_sector_size:
-
 
1246
        DEBUGF 1,'K : sector size %x is invalid\n',[esi+DISK.MediaInfo.SectorSize]
-
 
1247
        xor     eax, eax
-
 
1248
        ret
-
 
1249
.normal_sector_size:
-
 
1250
        mov     [esi+DISK.SysCache.sector_size_log], ecx
-
 
1251
        mov     [esi+DISK.AppCache.sector_size_log], ecx
1206
; 1. Calculate the suggested cache size.
1252
; 2. Calculate the suggested cache size.
1207
; 1a. Get the size of free physical memory in pages.
1253
; 2a. Get the size of free physical memory in pages.
1208
        mov     eax, [pg_data.pages_free]
1254
        mov     eax, [pg_data.pages_free]
1209
; 1b. Use the value to calculate the size.
1255
; 2b. Use the value to calculate the size.
1210
        shl     eax, 12 - 5     ; 1/32 of it in bytes
1256
        shl     eax, 12 - 5     ; 1/32 of it in bytes
1211
        and     eax, -8*4096    ; round down to the multiple of 8 pages
1257
        and     eax, -8*4096    ; round down to the multiple of 8 pages
1212
; 1c. Force lower and upper limits.
1258
; 2c. Force lower and upper limits.
1213
        cmp     eax, 1024*1024
1259
        cmp     eax, 1024*1024
1214
        jb      @f
1260
        jb      @f
1215
        mov     eax, 1024*1024
1261
        mov     eax, 1024*1024
1216
@@:
1262
@@:
1217
        cmp     eax, 128*1024
1263
        cmp     eax, 128*1024
1218
        ja      @f
1264
        ja      @f
1219
        mov     eax, 128*1024
1265
        mov     eax, 128*1024
1220
@@:
1266
@@:
1221
; 1d. Give a chance to the driver to adjust the size.
1267
; 2d. Give a chance to the driver to adjust the size.
1222
        push    eax
1268
        push    eax
1223
        mov     al, DISKFUNC.adjust_cache_size
1269
        mov     al, DISKFUNC.adjust_cache_size
1224
        call    disk_call_driver
1270
        call    disk_call_driver
1225
; Cache size calculated.
1271
; Cache size calculated.
1226
        mov     [esi+DISK.cache_size], eax
1272
        mov     [esi+DISK.cache_size], eax
1227
        test    eax, eax
1273
        test    eax, eax
1228
        jz      .nocache
1274
        jz      .nocache
1229
; 2. Allocate memory for the cache.
1275
; 3. Allocate memory for the cache.
1230
; 2a. Call the allocator.
1276
; 3a. Call the allocator.
1231
        stdcall kernel_alloc, eax
1277
        stdcall kernel_alloc, eax
1232
        test    eax, eax
1278
        test    eax, eax
1233
        jnz     @f
1279
        jnz     @f
1234
; 2b. If it failed, say a message and return with eax = 0.
1280
; 3b. If it failed, say a message and return with eax = 0.
1235
        dbgstr 'no memory for disk cache'
1281
        dbgstr 'no memory for disk cache'
1236
        jmp     .nothing
1282
        jmp     .nothing
1237
@@:
1283
@@:
1238
; 3. Fill two DISKCACHE structures.
1284
; 4. Fill two DISKCACHE structures.
1239
        mov     [esi+DISK.SysCache.pointer], eax
1285
        mov     [esi+DISK.SysCache.pointer], eax
1240
        lea     ecx, [esi+DISK.CacheLock]
1286
        lea     ecx, [esi+DISK.CacheLock]
1241
        call    mutex_init
1287
        call    mutex_init
1242
; The following code is inherited from getcache.inc.
1288
; The following code is inherited from getcache.inc.
1243
        mov     edx, [esi+DISK.SysCache.pointer]
1289
        mov     edx, [esi+DISK.SysCache.pointer]
1244
        and     [esi+DISK.SysCache.search_start], 0
1290
        and     [esi+DISK.SysCache.search_start], 0
1245
        and     [esi+DISK.AppCache.search_start], 0
1291
        and     [esi+DISK.AppCache.search_start], 0
1246
        mov     eax, [esi+DISK.cache_size]
1292
        mov     eax, [esi+DISK.cache_size]
1247
        shr     eax, 3
1293
        shr     eax, 3
1248
        mov     [esi+DISK.SysCache.data_size], eax
1294
        mov     [esi+DISK.SysCache.data_size], eax
1249
        add     edx, eax
1295
        add     edx, eax
1250
        imul    eax, 7
1296
        imul    eax, 7
1251
        mov     [esi+DISK.AppCache.data_size], eax
1297
        mov     [esi+DISK.AppCache.data_size], eax
1252
        mov     [esi+DISK.AppCache.pointer], edx
1298
        mov     [esi+DISK.AppCache.pointer], edx
1253
 
1299
 
1254
        mov     eax, [esi+DISK.SysCache.data_size]
1300
        mov     eax, [esi+DISK.SysCache.data_size]
1255
        push    ebx
-
 
1256
        call    calculate_for_hd64
1301
        call    calculate_cache_slots
1257
        pop     ebx
-
 
1258
        add     eax, [esi+DISK.SysCache.pointer]
1302
        add     eax, [esi+DISK.SysCache.pointer]
1259
        mov     [esi+DISK.SysCache.data], eax
1303
        mov     [esi+DISK.SysCache.data], eax
1260
        mov     [esi+DISK.SysCache.sad_size], ecx
1304
        mov     [esi+DISK.SysCache.sad_size], ecx
1261
 
1305
 
1262
        push    edi
1306
        push    edi
1263
        mov     edi, [esi+DISK.SysCache.pointer]
1307
        mov     edi, [esi+DISK.SysCache.pointer]
1264
        lea     ecx, [(ecx+1)*3]
1308
        lea     ecx, [(ecx+1)*3]
1265
        xor     eax, eax
1309
        xor     eax, eax
1266
        rep stosd
1310
        rep stosd
1267
        pop     edi
1311
        pop     edi
1268
 
1312
 
1269
        mov     eax, [esi+DISK.AppCache.data_size]
1313
        mov     eax, [esi+DISK.AppCache.data_size]
1270
        push    ebx
-
 
1271
        call    calculate_for_hd64
1314
        call    calculate_cache_slots
1272
        pop     ebx
-
 
1273
        add     eax, [esi+DISK.AppCache.pointer]
1315
        add     eax, [esi+DISK.AppCache.pointer]
1274
        mov     [esi+DISK.AppCache.data], eax
1316
        mov     [esi+DISK.AppCache.data], eax
1275
        mov     [esi+DISK.AppCache.sad_size], ecx
1317
        mov     [esi+DISK.AppCache.sad_size], ecx
1276
 
1318
 
1277
        push    edi
1319
        push    edi
1278
        mov     edi, [esi+DISK.AppCache.pointer]
1320
        mov     edi, [esi+DISK.AppCache.pointer]
1279
        lea     ecx, [(ecx+1)*3]
1321
        lea     ecx, [(ecx+1)*3]
1280
        xor     eax, eax
1322
        xor     eax, eax
1281
        rep stosd
1323
        rep stosd
1282
        pop     edi
1324
        pop     edi
1283
 
1325
 
1284
; 4. Return with nonzero al.
1326
; 5. Return with nonzero al.
1285
        mov     al, 1
1327
        mov     al, 1
1286
; 5. Return.
1328
; 6. Return.
1287
.nothing:
1329
.nothing:
1288
        ret
1330
        ret
1289
; No caching is required for this driver. Zero cache pointers and return with
1331
; No caching is required for this driver. Zero cache pointers and return with
1290
; nonzero al.
1332
; nonzero al.
1291
.nocache:
1333
.nocache:
1292
        mov     [esi+DISK.SysCache.pointer], eax
1334
        mov     [esi+DISK.SysCache.pointer], eax
1293
        mov     [esi+DISK.AppCache.pointer], eax
1335
        mov     [esi+DISK.AppCache.pointer], eax
1294
        mov     al, 1
1336
        mov     al, 1
1295
        ret
1337
        ret
1296
 
1338
 
1297
calculate_for_hd64:
1339
calculate_cache_slots:
1298
        push    eax
1340
        push    eax
1299
        mov     ebx, eax
1341
        mov     ecx, [esi+DISK.MediaInfo.SectorSize]
1300
        shr     eax, 9
1342
        add     ecx, sizeof.CACHE_ITEM
1301
        lea     eax, [eax*3]
1343
        xor     edx, edx
1302
        shl     eax, 2
1344
        div     ecx
1303
        sub     ebx, eax
1345
        mov     ecx, eax
1304
        shr     ebx, 9
1346
        imul    eax, [esi+DISK.MediaInfo.SectorSize]
1305
        mov     ecx, ebx
-
 
1306
        shl     ebx, 9
1347
        sub     [esp], eax
1307
        pop     eax
-
 
1308
        sub     eax, ebx
1348
        pop     eax
1309
        dec     ecx
1349
        dec     ecx
1310
        ret
1350
        ret
1311
 
1351
 
1312
 
1352
 
1313
; This internal function is called from disk_media_dereference to free the
1353
; This internal function is called from disk_media_dereference to free the
1314
; allocated cache, if there is one.
1354
; allocated cache, if there is one.
1315
; esi = pointer to DISK structure
1355
; esi = pointer to DISK structure
1316
disk_free_cache:
1356
disk_free_cache:
1317
; The algorithm is straightforward.
1357
; The algorithm is straightforward.
1318
        mov     eax, [esi+DISK.SysCache.pointer]
1358
        mov     eax, [esi+DISK.SysCache.pointer]
1319
        test    eax, eax
1359
        test    eax, eax
1320
        jz      .nothing
1360
        jz      .nothing
1321
        stdcall kernel_free, eax
1361
        stdcall kernel_free, eax
1322
.nothing:
1362
.nothing:
1323
        ret
1363
        ret
1324
 
1364
 
1325
; This function flushes all modified data from both caches for the given DISK.
1365
; This function flushes all modified data from both caches for the given DISK.
1326
; esi = pointer to DISK
1366
; esi = pointer to DISK
1327
disk_sync:
1367
disk_sync:
1328
; The algorithm is straightforward.
1368
; The algorithm is straightforward.
1329
        cmp     [esi+DISK.SysCache.pointer], 0
1369
        cmp     [esi+DISK.SysCache.pointer], 0
1330
        jz      .nothing
1370
        jz      .nothing
1331
        lea     ecx, [esi+DISK.CacheLock]
1371
        lea     ecx, [esi+DISK.CacheLock]
1332
        call    mutex_lock
1372
        call    mutex_lock
1333
        push    ebx
1373
        push    ebx
1334
        push    esi     ; for second write_cache64
1374
        push    esi     ; for second write_cache64
1335
        push    esi     ; for first write_cache64
1375
        push    esi     ; for first write_cache64
1336
        lea     ebx, [esi+DISK.SysCache]
1376
        lea     ebx, [esi+DISK.SysCache]
1337
        call    write_cache64
1377
        call    write_cache64
1338
        add     ebx, DISK.AppCache - DISK.SysCache
1378
        add     ebx, DISK.AppCache - DISK.SysCache
1339
        call    write_cache64
1379
        call    write_cache64
1340
        pop     ebx
1380
        pop     ebx
1341
        lea     ecx, [esi+DISK.CacheLock]
1381
        lea     ecx, [esi+DISK.CacheLock]
1342
        call    mutex_unlock
1382
        call    mutex_unlock
1343
.nothing:
1383
.nothing:
1344
        mov     al, DISKFUNC.flush
1384
        mov     al, DISKFUNC.flush
1345
        call    disk_call_driver
1385
        call    disk_call_driver
1346
        ret
1386
        ret