Subversion Repositories Kolibri OS

Rev

Rev 2455 | Rev 2653 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2288 clevermous 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
2455 mario79 3
;; Copyright (C) KolibriOS team 2011-2012. All rights reserved. ;;
2288 clevermous 4
;; Distributed under terms of the GNU General Public License    ;;
5
;;                                                              ;;
6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7
 
8
$Revision: 2643 $
9
 
10
; This function is intended to replace the old 'hd_read' function when
11
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
12
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
13
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
14
; eax is relative to partition start
15
; out: eax = error code; 0 = ok
16
fs_read32_sys:
17
; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure,
18
; this request should be processed by hd_read.
19
        cmp     [ebp+PARTITION.Disk], 'old'
20
        jnz     @f
2643 clevermous 21
        add     eax, dword [ebp+PARTITION.FirstSector]
2288 clevermous 22
        mov     [hdd_appl_data], 0
23
        call    hd_read
24
        mov     [hdd_appl_data], 1      ; restore to default state
2643 clevermous 25
        mov     eax, [hd_error]
2288 clevermous 26
        ret
27
@@:
28
; In the normal case, save ecx, set ecx to SysCache and let the common part
29
; do its work.
30
        push    ecx
31
        mov     ecx, [ebp+PARTITION.Disk]
32
        add     ecx, DISK.SysCache
33
        jmp     fs_read32_common
34
 
35
; This function is intended to replace the old 'hd_read' function when
36
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
37
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
38
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
39
; eax is relative to partition start
40
; out: eax = error code; 0 = ok
41
fs_read32_app:
42
; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure,
43
; this request should be processed by hd_read.
44
        cmp     [ebp+PARTITION.Disk], 'old'
45
        jnz     @f
2643 clevermous 46
        add     eax, dword [ebp+PARTITION.FirstSector]
2288 clevermous 47
        mov     [hdd_appl_data], 1
2643 clevermous 48
        call    hd_read
49
        mov     eax, [hd_error]
50
        ret
2288 clevermous 51
@@:
52
; In the normal case, save ecx, set ecx to AppCache and let the common part
53
; do its work.
54
        push    ecx
55
        mov     ecx, [ebp+PARTITION.Disk]
56
        add     ecx, DISK.AppCache
57
 
58
; This label is the common part of fs_read32_sys and fs_read32_app.
59
fs_read32_common:
60
; 1. Check that the required sector is inside the partition. If no, return
61
; DISK_STATUS_END_OF_MEDIA.
62
        cmp     dword [ebp+PARTITION.Length+4], 0
63
        jnz     @f
64
        cmp     dword [ebp+PARTITION.Length], eax
65
        ja      @f
66
        mov     eax, DISK_STATUS_END_OF_MEDIA
67
        pop     ecx
68
        ret
69
@@:
70
; 2. Get the absolute sector on the disk.
2643 clevermous 71
        push    edx esi
2288 clevermous 72
        xor     edx, edx
73
        add     eax, dword [ebp+PARTITION.FirstSector]
74
        adc     edx, dword [ebp+PARTITION.FirstSector+4]
75
; 3. If there is no cache for this disk, just pass the request to the driver.
76
        cmp     [ecx+DISKCACHE.pointer], 0
77
        jnz     .scancache
78
        push    1
79
        push    esp     ; numsectors
80
        push    edx     ; startsector
81
        push    eax     ; startsector
82
        push    ebx     ; buffer
2643 clevermous 83
        mov     esi, [ebp+PARTITION.Disk]
2288 clevermous 84
        mov     al, DISKFUNC.read
85
        call    disk_call_driver
86
        pop     ecx
2643 clevermous 87
        pop     esi edx
2288 clevermous 88
        pop     ecx
89
        ret
90
.scancache:
91
; 4. Scan the cache.
2643 clevermous 92
        push    edi ecx ; scan cache
2288 clevermous 93
        push    edx eax
94
virtual at esp
95
.sector_lo      dd      ?
96
.sector_hi      dd      ?
97
.cache          dd      ?
98
end virtual
99
; The following code is inherited from hd_read. The differences are:
100
; all code is protected by the cache lock; instead of static calls
101
; to hd_read_dma/hd_read_pio/bd_read the dynamic call to DISKFUNC.read is used;
102
; sector is 64-bit, not 32-bit.
103
        call    mutex_lock
104
        mov     eax, [.sector_lo]
105
        mov     edx, [.sector_hi]
106
        mov     esi, [ecx+DISKCACHE.pointer]
107
        mov     ecx, [ecx+DISKCACHE.sad_size]
108
        add     esi, 12
109
 
110
        mov     edi, 1
111
 
112
.hdreadcache:
113
 
114
        cmp     dword [esi+8], 0        ; empty
115
        je      .nohdcache
116
 
117
        cmp     [esi], eax      ; correct sector
118
        jne     .nohdcache
119
        cmp     [esi+4], edx    ; correct sector
120
        je      .yeshdcache
121
 
122
.nohdcache:
123
 
124
        add     esi, 12
125
        inc     edi
126
        dec     ecx
127
        jnz     .hdreadcache
128
 
129
        mov     esi, [.cache]
130
        call    find_empty_slot64       ; ret in edi
131
        test    eax, eax
132
        jnz     .read_done
133
 
134
        push    1
135
        push    esp
136
        push    edx
137
        push    [.sector_lo+12]
138
        mov     ecx, [.cache]
139
        mov     eax, edi
140
        shl     eax, 9
141
        add     eax, [ecx+DISKCACHE.data]
142
        push    eax
143
        mov     esi, [ebp+PARTITION.Disk]
144
        mov     al, DISKFUNC.read
145
        call    disk_call_driver
146
        pop     ecx
147
        dec     ecx
148
        jnz     .read_done
149
 
150
        mov     ecx, [.cache]
151
        lea     eax, [edi*3]
152
        mov     esi, [ecx+DISKCACHE.pointer]
153
        lea     esi, [eax*4+esi]
154
 
155
        mov     eax, [.sector_lo]
156
        mov     edx, [.sector_hi]
157
        mov     [esi], eax      ; sector number
158
        mov     [esi+4], edx    ; sector number
159
        mov     dword [esi+8], 1; hd read - mark as same as in hd
160
 
161
.yeshdcache:
162
 
163
        mov     esi, edi
164
        mov     ecx, [.cache]
165
        shl     esi, 9
166
        add     esi, [ecx+DISKCACHE.data]
167
 
168
        mov     edi, ebx
169
        mov     ecx, 512/4
170
        rep movsd               ; move data
171
        xor     eax, eax        ; successful read
172
.read_done:
173
        mov     ecx, [.cache]
174
        push    eax
175
        call    mutex_unlock
176
        pop     eax
177
        add     esp, 12
178
        pop     edi esi edx ecx
179
        ret
180
 
181
; This function is intended to replace the old 'hd_write' function when
182
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
183
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
184
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
185
; eax is relative to partition start
186
; out: eax = error code; 0 = ok
187
fs_write32_sys:
188
; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure,
189
; this request should be processed by hd_write.
190
        cmp     [ebp+PARTITION.Disk], 'old'
191
        jnz     @f
2643 clevermous 192
        add     eax, dword [ebp+PARTITION.FirstSector]
2288 clevermous 193
        mov     [hdd_appl_data], 0
194
        call    hd_write
195
        mov     [hdd_appl_data], 1      ; restore to default state
2643 clevermous 196
        mov     eax, [hd_error]
2288 clevermous 197
        ret
198
@@:
199
; In the normal case, save ecx, set ecx to SysCache and let the common part
200
; do its work.
201
        push    ecx
202
        mov     ecx, [ebp+PARTITION.Disk]
203
        add     ecx, DISK.SysCache
204
        jmp     fs_write32_common
205
 
206
; This function is intended to replace the old 'hd_write' function when
207
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
208
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
209
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
210
; eax is relative to partition start
211
; out: eax = error code; 0 = ok
212
fs_write32_app:
213
; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure,
214
; this request should be processed by hd_write.
215
        cmp     [ebp+PARTITION.Disk], 'old'
216
        jnz     @f
2643 clevermous 217
        add     eax, dword [ebp+PARTITION.FirstSector]
2288 clevermous 218
        mov     [hdd_appl_data], 1
2643 clevermous 219
        call    hd_write
220
        mov     eax, [hd_error]
221
        ret
2288 clevermous 222
@@:
223
; In the normal case, save ecx, set ecx to AppCache and let the common part
224
; do its work.
225
        push    ecx
226
        mov     ecx, [ebp+PARTITION.Disk]
227
        add     ecx, DISK.AppCache
228
 
229
; This label is the common part of fs_read32_sys and fs_read32_app.
230
fs_write32_common:
231
; 1. Check that the required sector is inside the partition. If no, return
232
; DISK_STATUS_END_OF_MEDIA.
233
        cmp     dword [ebp+PARTITION.Length+4], 0
234
        jnz     @f
235
        cmp     dword [ebp+PARTITION.Length], eax
236
        ja      @f
237
        mov     eax, DISK_STATUS_END_OF_MEDIA
238
        pop     ecx
239
        ret
240
@@:
2643 clevermous 241
        push    edx esi
2288 clevermous 242
; 2. Get the absolute sector on the disk.
243
        xor     edx, edx
244
        add     eax, dword [ebp+PARTITION.FirstSector]
245
        adc     edx, dword [ebp+PARTITION.FirstSector+4]
246
; 3. If there is no cache for this disk, just pass request to the driver.
247
        cmp     [ecx+DISKCACHE.pointer], 0
248
        jnz     .scancache
249
        push    1
250
        push    esp     ; numsectors
251
        push    edx     ; startsector
252
        push    eax     ; startsector
253
        push    ebx     ; buffer
2643 clevermous 254
        mov     esi, [ebp+PARTITION.Disk]
2288 clevermous 255
        mov     al, DISKFUNC.write
256
        call    disk_call_driver
257
        pop     ecx
2643 clevermous 258
        pop     esi edx
2288 clevermous 259
        pop     ecx
260
        ret
261
.scancache:
262
; 4. Scan the cache.
2643 clevermous 263
        push    edi ecx ; scan cache
2288 clevermous 264
        push    edx eax
265
virtual at esp
266
.sector_lo      dd      ?
267
.sector_hi      dd      ?
268
.cache          dd      ?
269
end virtual
270
; The following code is inherited from hd_write. The differences are:
271
; all code is protected by the cache lock;
272
; sector is 64-bit, not 32-bit.
273
        call    mutex_lock
274
 
275
        ; check if the cache already has the sector and overwrite it
276
        mov     eax, [.sector_lo]
277
        mov     edx, [.sector_hi]
278
        mov     esi, [ecx+DISKCACHE.pointer]
279
        mov     ecx, [ecx+DISKCACHE.sad_size]
280
        add     esi, 12
281
 
282
        mov     edi, 1
283
 
284
.hdwritecache:
285
        cmp     dword [esi+8], 0        ; if cache slot is empty
286
        je      .not_in_cache_write
287
 
288
        cmp     [esi], eax      ; if the slot has the sector
289
        jne     .not_in_cache_write
290
        cmp     [esi+4], edx    ; if the slot has the sector
291
        je      .yes_in_cache_write
292
 
293
.not_in_cache_write:
294
 
295
        add     esi, 12
296
        inc     edi
297
        dec     ecx
298
        jnz     .hdwritecache
299
 
300
        ; sector not found in cache
301
        ; write the block to a new location
302
 
303
        mov     esi, [.cache]
304
        call    find_empty_slot64       ; ret in edi
305
        test    eax, eax
306
        jne     .hd_write_access_denied
307
 
308
        mov     ecx, [.cache]
309
        lea     eax, [edi*3]
310
        mov     esi, [ecx+DISKCACHE.pointer]
311
        lea     esi, [eax*4+esi]
312
 
313
        mov     eax, [.sector_lo]
314
        mov     edx, [.sector_hi]
315
        mov     [esi], eax      ; sector number
316
        mov     [esi+4], edx    ; sector number
317
 
318
.yes_in_cache_write:
319
 
320
        mov     dword [esi+4], 2        ; write - differs from hd
321
 
322
        shl     edi, 9
323
        mov     ecx, [.cache]
324
        add     edi, [ecx+DISKCACHE.data]
325
 
326
        mov     esi, ebx
327
        mov     ecx, 512/4
328
        rep movsd               ; move data
329
        xor     eax, eax        ; success
330
.hd_write_access_denied:
331
        mov     ecx, [.cache]
332
        push    eax
333
        call    mutex_unlock
334
        pop     eax
335
        add     esp, 12
336
        pop     edi esi edx ecx
337
        ret
338
 
339
; This internal function is called from fs_read32_* and fs_write32_*. It is the
340
; analogue of find_empty_slot for 64-bit sectors.
341
find_empty_slot64:
342
;-----------------------------------------------------------
343
; find empty or read slot, flush cache if next 12.5% is used by write
344
; output : edi = cache slot
345
;-----------------------------------------------------------
346
.search_again:
347
        mov     ecx, [esi+DISKCACHE.sad_size]
348
        mov     edi, [esi+DISKCACHE.search_start]
349
        shr     ecx, 3
350
.search_for_empty:
351
        inc     edi
352
        cmp     edi, [esi+DISKCACHE.sad_size]
353
        jbe     .inside_cache
354
        mov     edi, 1
355
.inside_cache:
356
        lea     eax, [edi*3]
357
        shl     eax, 2
358
        add     eax, [esi+DISKCACHE.pointer]
359
        cmp     dword [eax+8], 2
360
        jb      .found_slot             ; it's empty or read
361
        dec     ecx
362
        jnz     .search_for_empty
2643 clevermous 363
        stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all
2288 clevermous 364
        test    eax, eax
365
        jne     .found_slot_access_denied
366
        jmp     .search_again           ; and start again
367
.found_slot:
368
        mov     [esi+DISKCACHE.search_start], edi
369
        xor     eax, eax        ; success
370
.found_slot_access_denied:
371
        ret
372
 
373
; This function is intended to replace the old 'write_cache' function.
2643 clevermous 374
proc write_cache64 uses ecx edx esi edi, disk:dword
2288 clevermous 375
locals
376
cache_chain_started     dd      ?
377
cache_chain_size        dd      ?
378
cache_chain_pos         dd      ?
379
cache_chain_ptr         dd      ?
380
endl
381
; If there is no cache for this disk, nothing to do.
382
        cmp     [esi+DISKCACHE.pointer], 0
383
        jz      .flush
384
;-----------------------------------------------------------
385
; write all changed sectors to disk
386
;-----------------------------------------------------------
387
 
388
        ; write difference ( 2 ) from cache to DISK
389
        mov     ecx, [esi+DISKCACHE.sad_size]
390
        mov     esi, [esi+DISKCACHE.pointer]
391
        add     esi, 12
392
        mov     edi, 1
393
.write_cache_more:
394
        cmp     dword [esi+8], 2        ; if cache slot is not different
395
        jne     .write_chain
396
        mov     dword [esi+8], 1        ; same as in hd
397
        mov     eax, [esi]
398
        mov     edx, [esi+4]            ; edx:eax = sector to write
399
; Объединяем запись цепочки последовательных секторов в одно обращение к диску
400
        cmp     ecx, 1
401
        jz      .nonext
402
        cmp     dword [esi+12+8], 2
403
        jnz     .nonext
404
        push    eax edx
405
        add     eax, 1
406
        adc     edx, 0
407
        cmp     eax, [esi+12]
408
        jnz     @f
409
        cmp     edx, [esi+12+4]
410
@@:
411
        pop     edx eax
412
        jnz     .nonext
413
        cmp     [cache_chain_started], 1
414
        jz      @f
415
        mov     [cache_chain_started], 1
416
        mov     [cache_chain_size], 0
417
        mov     [cache_chain_pos], edi
418
        mov     [cache_chain_ptr], esi
419
@@:
420
        inc     [cache_chain_size]
421
        cmp     [cache_chain_size], 16
422
        jnz     .continue
423
        jmp     .write_chain
424
.nonext:
425
        call    .flush_cache_chain
426
        test    eax, eax
427
        jnz     .nothing
428
        mov     [cache_chain_size], 1
429
        mov     [cache_chain_ptr], esi
430
        call    .write_cache_sector
431
        test    eax, eax
432
        jnz     .nothing
433
        jmp     .continue
434
.write_chain:
435
        call    .flush_cache_chain
436
        test    eax, eax
437
        jnz     .nothing
438
.continue:
439
        add     esi, 12
440
        inc     edi
441
        dec     ecx
442
        jnz     .write_cache_more
443
        call    .flush_cache_chain
444
        test    eax, eax
445
        jnz     .nothing
446
.flush:
2643 clevermous 447
        mov     esi, [disk]
2288 clevermous 448
        mov     al, DISKFUNC.flush
449
        call    disk_call_driver
450
.nothing:
451
        ret
452
 
453
.flush_cache_chain:
454
        xor     eax, eax
455
        cmp     [cache_chain_started], eax
456
        jz      @f
457
        call    .write_cache_chain
458
        mov     [cache_chain_started], 0
459
@@:
460
        retn
461
 
462
.write_cache_sector:
463
        mov     [cache_chain_size], 1
464
        mov     [cache_chain_pos], edi
465
.write_cache_chain:
466
        pusha
467
        mov     edi, [cache_chain_pos]
468
        mov     ecx, [ebp-12]
469
        shl     edi, 9
470
        add     edi, [ecx+DISKCACHE.data]
471
        mov     ecx, [cache_chain_size]
472
        push    ecx
473
        push    esp     ; numsectors
474
        mov     eax, [cache_chain_ptr]
475
        pushd   [eax+4]
476
        pushd   [eax]   ; startsector
477
        push    edi     ; buffer
478
        mov     esi, [ebp]
479
        mov     esi, [esi+PARTITION.Disk]
480
        mov     al, DISKFUNC.write
481
        call    disk_call_driver
482
        pop     ecx
483
        mov     [esp+28], eax
484
        popa
485
        retn
486
endp
487
 
488
; This internal function is called from disk_add to initialize the caching for
489
; a new DISK.
490
; The algorithm is inherited from getcache.inc: take 1/32 part of the available
491
; physical memory, round down to 8 pages, limit by 128K from below and by 1M
492
; from above. Reserve 1/8 part of the cache for system data and 7/8 for app
493
; data.
494
; After the size is calculated, but before the cache is allocated, the device
495
; driver can adjust the size. In particular, setting size to zero disables
496
; caching: there is no sense in a cache for a ramdisk. In fact, such action
497
; is most useful example of a non-trivial adjustment.
498
; esi = pointer to DISK structure
499
disk_init_cache:
500
; 1. Calculate the suggested cache size.
501
; 1a. Get the size of free physical memory in pages.
502
        mov     eax, [pg_data.pages_free]
503
; 1b. Use the value to calculate the size.
504
        shl     eax, 12 - 5     ; 1/32 of it in bytes
505
        and     eax, -8*4096    ; round down to the multiple of 8 pages
506
; 1c. Force lower and upper limits.
507
        cmp     eax, 1024*1024
508
        jb      @f
509
        mov     eax, 1024*1024
510
@@:
511
        cmp     eax, 128*1024
512
        ja      @f
513
        mov     eax, 128*1024
514
@@:
515
; 1d. Give a chance to the driver to adjust the size.
516
        push    eax
517
        mov     al, DISKFUNC.adjust_cache_size
518
        call    disk_call_driver
519
; Cache size calculated.
520
        mov     [esi+DISK.cache_size], eax
521
        test    eax, eax
522
        jz      .nocache
523
; 2. Allocate memory for the cache.
524
; 2a. Call the allocator.
525
        stdcall kernel_alloc, eax
526
        test    eax, eax
527
        jnz     @f
528
; 2b. If it failed, say a message and return with eax = 0.
529
        dbgstr 'no memory for disk cache'
530
        jmp     .nothing
531
@@:
532
; 3. Fill two DISKCACHE structures.
533
        mov     [esi+DISK.SysCache.pointer], eax
2381 hidnplayr 534
        lea     ecx, [esi+DISK.SysCache.mutex]
2288 clevermous 535
        call    mutex_init
2381 hidnplayr 536
        lea     ecx, [esi+DISK.AppCache.mutex]
2288 clevermous 537
        call    mutex_init
538
; The following code is inherited from getcache.inc.
539
        mov     edx, [esi+DISK.SysCache.pointer]
540
        and     [esi+DISK.SysCache.search_start], 0
541
        and     [esi+DISK.AppCache.search_start], 0
542
        mov     eax, [esi+DISK.cache_size]
543
        shr     eax, 3
544
        mov     [esi+DISK.SysCache.data_size], eax
545
        add     edx, eax
546
        imul    eax, 7
547
        mov     [esi+DISK.AppCache.data_size], eax
548
        mov     [esi+DISK.AppCache.pointer], edx
549
 
550
        mov     eax, [esi+DISK.SysCache.data_size]
551
        push    ebx
552
        call    calculate_for_hd
553
        pop     ebx
554
        add     eax, [esi+DISK.SysCache.pointer]
555
        mov     [esi+DISK.SysCache.data], eax
556
        mov     [esi+DISK.SysCache.sad_size], ecx
557
 
558
        push    edi
559
        mov     edi, [esi+DISK.SysCache.pointer]
560
        lea     ecx, [ecx*3]
561
        xor     eax, eax
562
        rep stosd
563
        pop     edi
564
 
565
        mov     eax, [esi+DISK.AppCache.data_size]
566
        push    ebx
567
        call    calculate_for_hd
568
        pop     ebx
569
        add     eax, [esi+DISK.AppCache.pointer]
570
        mov     [esi+DISK.AppCache.data], eax
571
        mov     [esi+DISK.AppCache.sad_size], ecx
572
 
573
        push    edi
574
        mov     edi, [esi+DISK.AppCache.pointer]
575
        lea     ecx, [ecx*3]
576
        xor     eax, eax
577
        rep stosd
578
        pop     edi
579
 
580
; 4. Return with nonzero al.
581
        mov     al, 1
582
; 5. Return.
583
.nothing:
584
        ret
585
; No caching is required for this driver. Zero cache pointers and return with
586
; nonzero al.
587
.nocache:
588
        mov     [esi+DISK.SysCache.pointer], eax
589
        mov     [esi+DISK.AppCache.pointer], eax
590
        mov     al, 1
591
        ret
592
 
593
; This internal function is called from disk_media_dereference to free the
594
; allocated cache, if there is one.
595
; esi = pointer to DISK structure
596
disk_free_cache:
597
; The algorithm is straightforward.
598
        mov     eax, [esi+DISK.SysCache.pointer]
599
        test    eax, eax
600
        jz      .nothing
601
        stdcall kernel_free, eax
602
.nothing:
603
        ret
2643 clevermous 604
 
605
; This function flushes all modified data from both caches for the given DISK.
606
; esi = pointer to DISK
607
disk_sync:
608
; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure,
609
; this request should be processed by write_cache.
610
        cmp     esi, 'old'
611
        jz      write_cache
612
; The algorithm is straightforward.
613
        push    esi
614
        push    esi     ; for second write_cache64
615
        push    esi     ; for first write_cache64
616
        add     esi, DISK.SysCache
617
        call    write_cache64
618
        add     esi, DISK.AppCache - DISK.SysCache
619
        call    write_cache64
620
        pop     esi
621
        ret