Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
425 victor 1
$Revision: 521 $
431 serge 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3
;;                                                              ;;
4
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;;
5
;; Distributed under terms of the GNU General Public License    ;;
6
;;                                                              ;;
7
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
94 mario79 8
 
87 mario79 9
uglobal
10
cd_current_pointer_of_input    dd  0
11
cd_current_pointer_of_input_2  dd  0
12
cd_mem_location                dd  0
13
cd_counter_block               dd  0
95 mario79 14
IDE_Channel_1                  db  0
15
IDE_Channel_2                  db  0
87 mario79 16
endg
17
 
18
CDDataBuf equ 0x7000
19
 
20
reserve_cd:
21
 
22
    cli
23
    cmp   [cd_status],0
24
    je    reserve_ok2
25
 
26
    sti
27
    call  change_task
95 mario79 28
    jmp   reserve_cd
87 mario79 29
 
30
  reserve_ok2:
31
 
32
    push  eax
379 serge 33
    mov   eax,[CURRENT_TASK]
87 mario79 34
    shl   eax,5
379 serge 35
    mov   eax,[eax+CURRENT_TASK+TASKDATA.pid]
87 mario79 36
    mov   [cd_status],eax
37
    pop   eax
38
    sti
39
    ret
95 mario79 40
 
41
reserve_cd_channel:
42
    cmp   [ChannelNumber],1
43
    jne   .IDE_Channel_2
44
.IDE_Channel_1:
45
    cli
46
    cmp   [IDE_Channel_1],0
47
    je    .reserve_ok_1
48
    sti
49
    call  change_task
50
    jmp   .IDE_Channel_1
51
.IDE_Channel_2:
52
    cli
53
    cmp   [IDE_Channel_2],0
54
    je    .reserve_ok_2
55
    sti
56
    call  change_task
57
    jmp   .IDE_Channel_1
58
.reserve_ok_1:
59
    mov [IDE_Channel_1],1
60
    ret
61
.reserve_ok_2:
62
    mov [IDE_Channel_2],1
63
    ret
64
 
65
free_cd_channel:
66
    cmp   [ChannelNumber],1
67
    jne   .IDE_Channel_2
68
.IDE_Channel_1:
379 serge 69
    mov [IDE_Channel_1],0
95 mario79 70
    ret
71
.IDE_Channel_2:
72
    mov [IDE_Channel_2],0
73
    ret
364 diamond 74
 
379 serge 75
uglobal
87 mario79 76
cd_status dd 0
364 diamond 77
endg
87 mario79 78
 
79
;----------------------------------------------------------------
80
;
81
;  fs_CdRead - LFN variant for reading CD disk
82
;
83
;  esi  points to filename /dir1/dir2/.../dirn/file,0
84
;  ebx  pointer to 64-bit number = first wanted byte, 0+
85
;       may be ebx=0 - start from first byte
86
;  ecx  number of bytes to read, 0+
87
;  edx  mem location to return data
88
;
89
;  ret ebx = bytes read or 0xffffffff file not found
90
;      eax = 0 ok read or other = errormsg
91
;
92
;--------------------------------------------------------------
93
fs_CdRead:
94
    push    edi
95
    cmp    byte [esi], 0
96
    jnz    @f
97
.noaccess:
98
    pop    edi
99
.noaccess_2:
100
    or    ebx, -1
101
    mov    eax, ERROR_ACCESS_DENIED
102
    ret
379 serge 103
 
87 mario79 104
.noaccess_3:
105
    pop     eax edx ecx edi
106
    jmp  .noaccess_2
107
 
108
@@:
109
    call    cd_find_lfn
110
    jnc    .found
111
    pop    edi
112
    cmp   [DevErrorCode],0
113
    jne   .noaccess_2
114
    or    ebx, -1
115
    mov    eax, ERROR_FILE_NOT_FOUND
116
    ret
117
 
118
.found:
119
    mov    edi,[cd_current_pointer_of_input]
120
    test   byte [edi+25],10b    ; do not allow read directories
121
    jnz    .noaccess
122
    test    ebx, ebx
123
    jz    .l1
124
    cmp    dword [ebx+4], 0
125
    jz    @f
126
        xor     ebx, ebx
127
.reteof:
128
    mov    eax, 6 ; end of file
129
    pop    edi
130
    ret
131
@@:
132
    mov    ebx, [ebx]
133
.l1:
134
        push    ecx edx
135
        push    0
136
        mov     eax, [edi+10] ; реальный размер файловой секции
137
        sub     eax, ebx
138
        jb      .eof
139
        cmp     eax, ecx
140
        jae     @f
141
        mov     ecx, eax
142
        mov     byte [esp], 6
143
@@:
144
     mov    eax,[edi+2]
145
     mov    [CDSectorAddress],eax
146
; now eax=cluster, ebx=position, ecx=count, edx=buffer for data
147
.new_sector:
148
    test    ecx, ecx
149
    jz    .done
150
    sub    ebx, 2048
154 diamond 151
    jae    .next
87 mario79 152
    add    ebx, 2048
153
    jnz    .incomplete_sector
154
    cmp    ecx, 2048
155
    jb    .incomplete_sector
156
; we may read and memmove complete sector
157
    mov  [CDDataBuf_pointer],edx
158
    call ReadCDWRetr      ; читаем сектор файла
159
    cmp   [DevErrorCode],0
160
    jne   .noaccess_3
161
    add    edx, 2048
162
    sub    ecx, 2048
154 diamond 163
.next:
164
    inc  dword [CDSectorAddress]
87 mario79 165
    jmp    .new_sector
166
.incomplete_sector:
167
; we must read and memmove incomplete sector
168
    mov  [CDDataBuf_pointer],CDDataBuf
169
    call ReadCDWRetr      ; читаем сектор файла
170
    cmp   [DevErrorCode],0
379 serge 171
    jne   .noaccess_3
87 mario79 172
    push    ecx
173
    add    ecx, ebx
174
    cmp    ecx, 2048
175
    jbe    @f
176
    mov    ecx, 2048
177
@@:
178
    sub    ecx, ebx
179
     push edi esi ecx
180
     mov edi,edx
154 diamond 181
        lea     esi, [CDDataBuf + ebx]
87 mario79 182
     cld
183
     rep movsb
184
     pop ecx esi edi
185
    add    edx, ecx
186
    sub    [esp], ecx
187
    pop    ecx
188
    xor    ebx, ebx
154 diamond 189
    jmp    .next
379 serge 190
 
87 mario79 191
.done:
192
        mov     ebx, edx
193
        pop     eax edx ecx edi
194
        sub     ebx, edx
195
        ret
196
.eof:
197
        mov     ebx, edx
198
        pop     eax edx ecx
199
        sub     ebx, edx
200
        jmp     .reteof
201
 
202
;----------------------------------------------------------------
203
;
204
;  fs_CdReadFolder - LFN variant for reading CD disk folder
205
;
206
;  esi  points to filename  /dir1/dir2/.../dirn/file,0
207
;  ebx  pointer to structure 32-bit number = first wanted block, 0+
208
;                          & flags (bitfields)
209
; flags: bit 0: 0=ANSI names, 1=UNICODE names
210
;  ecx  number of blocks to read, 0+
211
;  edx  mem location to return data
212
;
213
;  ret ebx = blocks read or 0xffffffff folder not found
214
;      eax = 0 ok read or other = errormsg
215
;
216
;--------------------------------------------------------------
217
fs_CdReadFolder:
218
        push    edi
219
        call    cd_find_lfn
220
        jnc     .found
221
        pop     edi
364 diamond 222
        cmp     [DevErrorCode], 0
87 mario79 223
        jne     .noaccess_1
224
        or      ebx, -1
225
        mov     eax, ERROR_FILE_NOT_FOUND
226
        ret
227
.found:
364 diamond 228
        mov     edi, [cd_current_pointer_of_input]
229
        test    byte [edi+25], 10b    ; do not allow read directories
87 mario79 230
        jnz     .found_dir
231
        pop     edi
232
.noaccess_1:
233
        or      ebx, -1
234
        mov     eax, ERROR_ACCESS_DENIED
235
        ret
236
.found_dir:
364 diamond 237
        mov     eax, [edi+2]    ; eax=cluster
238
        mov     [CDSectorAddress], eax
239
        mov     eax, [edi+10]   ; размер директрории
87 mario79 240
.doit:
241
; init header
242
        push    eax ecx
243
        mov     edi, edx
244
        mov     ecx, 32/4
245
        xor     eax, eax
246
        rep     stosd
247
        pop     ecx eax
248
        mov     byte [edx], 1   ; version
364 diamond 249
        mov     [cd_mem_location], edx
379 serge 250
        add     [cd_mem_location], 32
87 mario79 251
; начинаем переброску БДВК в УСВК
252
;.mainloop:
364 diamond 253
        mov     [cd_counter_block], dword 0
254
        dec     dword [CDSectorAddress]
255
        push    ecx
87 mario79 256
.read_to_buffer:
364 diamond 257
        inc     dword [CDSectorAddress]
258
        mov     [CDDataBuf_pointer], CDDataBuf
259
        call    ReadCDWRetr         ; читаем сектор директории
260
        cmp     [DevErrorCode], 0
261
        jne     .noaccess_1
262
        call    .get_names_from_buffer
263
        sub     eax,2048
87 mario79 264
; директория закончилась?
364 diamond 265
        ja      .read_to_buffer
266
        mov     edi, [cd_counter_block]
267
        mov     [edx+8], edi
268
        mov     edi, [ebx]
269
        sub     [edx+4], edi
270
        xor     eax, eax
271
        dec     ecx
272
        js      @f
273
        mov     al, ERROR_END_OF_FILE
274
@@:
87 mario79 275
        pop     ecx edi
89 diamond 276
        mov     ebx, [edx+4]
364 diamond 277
        ret
278
 
87 mario79 279
.get_names_from_buffer:
280
    mov     [cd_current_pointer_of_input_2],CDDataBuf
281
    push    eax esi edi edx
282
.get_names_from_buffer_1:
283
    call    cd_get_name
284
    jc    .end_buffer
285
    inc    dword [cd_counter_block]
286
    mov    eax,[cd_counter_block]
287
    cmp    [ebx],eax
288
    jae     .get_names_from_buffer_1
289
    test    ecx, ecx
290
    jz    .get_names_from_buffer_1
291
    mov   edi,[cd_counter_block]
379 serge 292
    mov   [edx+4],edi
87 mario79 293
    dec     ecx
294
    mov   esi,ebp
295
    mov   edi,[cd_mem_location]
296
    add   edi,40
297
    test   dword [ebx+4], 1 ; 0=ANSI, 1=UNICODE
298
    jnz    .unicode
299
;    jmp  .unicode
300
.ansi:
301
    cmp   [cd_counter_block],2
302
    jbe   .ansi_parent_directory
303
    cld
304
    lodsw
305
    xchg ah,al
306
    call uni2ansi_char
307
    cld
308
    stosb
309
; проверка конца файла
96 mario79 310
    mov   ax,[esi]
311
    cmp   ax,word 3B00h ; сепаратор конца файла ';'
87 mario79 312
    je   .cd_get_parameters_of_file_1
313
; проверка для файлов не заканчивающихся сепаратором
314
    movzx   eax,byte [ebp-33]
315
    add   eax,ebp
316
    sub   eax,34
317
    cmp   esi,eax
318
    je   .cd_get_parameters_of_file_1
319
; проверка конца папки
320
    movzx   eax,byte [ebp-1]
321
    add   eax,ebp
322
    cmp   esi,eax
323
    jb   .ansi
324
.cd_get_parameters_of_file_1:
325
    mov   [edi],byte 0
90 mario79 326
    call  cd_get_parameters_of_file
87 mario79 327
    add   [cd_mem_location],304
328
    jmp   .get_names_from_buffer_1
329
 
330
.ansi_parent_directory:
331
    cmp   [cd_counter_block],2
332
    je    @f
333
    mov   [edi],byte '.'
334
    inc   edi
335
    jmp  .cd_get_parameters_of_file_1
336
@@:
337
    mov   [edi],word '..'
338
    add   edi,2
339
    jmp  .cd_get_parameters_of_file_1
340
 
341
.unicode:
342
    cmp   [cd_counter_block],2
343
    jbe   .unicode_parent_directory
344
    cld
345
    movsw
346
; проверка конца файла
347
    mov   ax,[esi]
348
    cmp   ax,word 3B00h ; сепаратор конца файла ';'
349
    je   .cd_get_parameters_of_file_2
350
; проверка для файлов не заканчивающихся сепаратором
351
    movzx   eax,byte [ebp-33]
352
    add   eax,ebp
353
    sub   eax,34
354
    cmp   esi,eax
355
    je   .cd_get_parameters_of_file_2
356
; проверка конца папки
357
    movzx   eax,byte [ebp-1]
358
    add   eax,ebp
359
    cmp   esi,eax
360
    jb   .unicode
361
.cd_get_parameters_of_file_2:
362
    mov   [edi],word 0
90 mario79 363
    call  cd_get_parameters_of_file
87 mario79 364
    add   [cd_mem_location],560
365
    jmp   .get_names_from_buffer_1
366
 
367
.unicode_parent_directory:
368
    cmp   [cd_counter_block],2
369
    je    @f
370
    mov   [edi],word 2E00h ; '.'
371
    add   edi,2
372
    jmp   .cd_get_parameters_of_file_2
373
@@:
374
    mov   [edi],dword 2E002E00h ; '..'
375
    add   edi,4
376
    jmp   .cd_get_parameters_of_file_2
377
 
90 mario79 378
.end_buffer:
379
    pop   edx edi esi eax
380
    ret
381
 
382
cd_get_parameters_of_file:
87 mario79 383
    mov   edi,[cd_mem_location]
90 mario79 384
cd_get_parameters_of_file_1:
87 mario79 385
; получаем атрибуты файла
386
    xor   eax,eax
379 serge 387
; файл не архивировалс
87 mario79 388
    inc   al
389
    shl   eax,1
390
; это каталог?
391
    test  [ebp-8],byte 2
392
    jz    .file
393
    inc   al
394
.file:
395
; метка тома не как в FAT, в этом виде отсутсвует
396
; файл не является системным
397
    shl   eax,3
398
; файл является скрытым? (атрибут существование)
399
    test  [ebp-8],byte 1
400
    jz    .hidden
401
    inc   al
402
.hidden:
403
    shl   eax,1
404
; файл всегда только для чтения, так как это CD
405
    inc   al
406
    mov   [edi],eax
407
; получаем время для файла
408
;час
409
    movzx eax,byte [ebp-12]
410
    shl   eax,8
411
;минута
412
    mov   al,[ebp-11]
413
    shl   eax,8
414
;секунда
415
    mov   al,[ebp-10]
416
;время создания файла
417
    mov   [edi+8],eax
418
;время последнего доступа
419
    mov   [edi+16],eax
420
;время последней записи
421
    mov   [edi+24],eax
422
; получаем дату для файла
423
;год
424
    movzx eax,byte [ebp-15]
425
    add   eax,1900
426
    shl   eax,8
427
;месяц
428
    mov   al,[ebp-14]
429
    shl   eax,8
430
;день
431
    mov   al,[ebp-13]
432
;дата создания файла
433
    mov   [edi+12],eax
434
;время последнего доступа
435
    mov   [edi+20],eax
436
;время последней записи
437
    mov   [edi+28],eax
438
; получаем тип данных имени
439
    xor   eax,eax
440
    test   dword [ebx+4], 1 ; 0=ANSI, 1=UNICODE
441
    jnz    .unicode_1
442
    mov    [edi+4],eax
443
    jmp   @f
444
.unicode_1:
445
    inc    eax
446
    mov    [edi+4],eax
447
@@:
448
; получаем размер файла в байтах
449
    xor   eax,eax
450
    mov   [edi+32+4],eax
451
    mov   eax,[ebp-23]
452
    mov   [edi+32],eax
453
    ret
454
 
90 mario79 455
;----------------------------------------------------------------
456
;
457
;  fs_CdGetFileInfo - LFN variant for CD
458
;                     get file/directory attributes structure
379 serge 459
;
90 mario79 460
;----------------------------------------------------------------
461
fs_CdGetFileInfo:
462
        cmp     byte [esi], 0
463
        jnz     @f
464
        mov     eax, 2
465
        ret
466
@@:
467
        push    edi ebp
468
        call    cd_find_lfn
469
        pushfd
470
        cmp     [DevErrorCode], 0
471
        jz      @f
472
        popfd
473
        pop     ebp edi
474
        mov     eax, 11
475
        ret
476
@@:
477
        popfd
478
        jnc     @f
479
        pop     ebp edi
480
        mov     eax, ERROR_FILE_NOT_FOUND
481
        ret
482
@@:
483
 
484
        mov     edi, edx
485
        call    cd_get_parameters_of_file_1
486
        and     dword [edi+4], 0
487
        pop     ebp edi
488
        xor     eax, eax
489
        ret
490
 
87 mario79 491
cd_find_lfn:
521 diamond 492
; in: esi+ebp -> name
87 mario79 493
; out: CF=1 - file not found
494
;      else CF=0 and [cd_current_pointer_of_input] direntry
495
        push eax esi
496
; 16 сектор начало набора дескрипторов томов
497
        mov  [CDSectorAddress],dword 15
498
.start:
499
        inc  dword [CDSectorAddress]
500
       mov  [CDDataBuf_pointer],CDDataBuf
501
       call  ReadCDWRetr
502
       cmp   [DevErrorCode],0
503
       jne   .access_denied
504
; проверка на вшивость
505
        cmp  [CDDataBuf+1],dword 'CD00'
506
        jne  .access_denied
507
        cmp  [CDDataBuf+5],byte '1'
508
        jne  .access_denied
509
; сектор является терминатором набор дескрипторов томов?
510
        cmp  [CDDataBuf],byte 0xff
379 serge 511
        je  .access_denied
87 mario79 512
; сектор является дополнительным и улучшенным дескриптором тома?
513
        cmp  [CDDataBuf],byte 0x2
379 serge 514
        jne  .start
87 mario79 515
; сектор является дополнительным дескриптором тома?
516
        cmp  [CDDataBuf+6],byte 0x1
517
        jne  .start
518
; параметры root директрории
519
        mov  eax,[CDDataBuf+0x9c+2] ; начало root директрории
520
        mov  [CDSectorAddress],eax
521
        mov  eax,[CDDataBuf+0x9c+10] ; размер root директрории
522
        cmp    byte [esi], 0
523
        jnz    @f
524
        mov   [cd_current_pointer_of_input],CDDataBuf+0x9c
525
        jmp   .done
526
@@:
527
; начинаем поиск
528
.mainloop:
529
        dec  dword [CDSectorAddress]
530
.read_to_buffer:
531
        inc  dword [CDSectorAddress]
532
        mov  [CDDataBuf_pointer],CDDataBuf
533
        call ReadCDWRetr         ; читаем сектор директории
534
        cmp   [DevErrorCode],0
535
        jne   .access_denied
536
        call cd_find_name_in_buffer
537
        jnc    .found
538
        sub  eax,2048
539
; директория закончилась?
540
        cmp  eax,0
99 mario79 541
        ja   .read_to_buffer
87 mario79 542
; нет искомого элемента цепочки
543
.access_denied:
544
        pop  esi eax
545
        stc
546
        ret
547
; искомый элемент цепочки найден
548
  .found:
549
; конец пути файла
94 mario79 550
        cmp    byte [esi-1], 0
87 mario79 551
        jz    .done
521 diamond 552
  .nested:
87 mario79 553
        mov    eax,[cd_current_pointer_of_input]
364 diamond 554
        push    dword [eax+2]
555
        pop     dword [CDSectorAddress]       ; начало директории
556
        mov    eax,[eax+2+8]  ; размер директории
87 mario79 557
        jmp    .mainloop
558
; указатель файла найден
559
   .done:
521 diamond 560
        test    ebp, ebp
561
        jz      @f
562
        mov     esi, ebp
563
        xor     ebp, ebp
564
        jmp     .nested
565
@@:
87 mario79 566
        pop  esi eax
567
        clc
568
        ret
379 serge 569
 
87 mario79 570
cd_find_name_in_buffer:
571
        mov     [cd_current_pointer_of_input_2],CDDataBuf
572
.start:
573
        call    cd_get_name
574
        jc    .not_found
575
        call    cd_compare_name
576
        jc    .start
379 serge 577
.found:
87 mario79 578
        clc
379 serge 579
        ret
87 mario79 580
.not_found:
581
        stc
582
        ret
583
 
584
cd_get_name:
585
        push eax
586
        mov   ebp,[cd_current_pointer_of_input_2]
587
        mov   [cd_current_pointer_of_input],ebp
588
        mov   eax,[ebp]
589
        cmp   eax,0   ; входы закончились?
590
        je    .next_sector
591
        cmp   ebp,CDDataBuf+2048     ; буфер закончился?
592
        jae   .next_sector
593
        movzx eax, byte [ebp]
594
        add   [cd_current_pointer_of_input_2],eax ; следующий вход каталога
595
        add   ebp,33 ; указатель установлен на начало имени
596
        pop   eax
597
        clc
598
        ret
599
.next_sector:
600
        pop  eax
601
        stc
602
        ret
603
 
604
cd_compare_name:
605
; compares ASCIIZ-names, case-insensitive (cp866 encoding)
606
; in: esi->name, ebp->name
607
; out: if names match: ZF=1 and esi->next component of name
608
;      else: ZF=0, esi is not changed
609
; destroys eax
379 serge 610
    push    esi eax edi
87 mario79 611
    mov     edi,ebp
612
.loop:
613
    cld
614
    lodsb
615
    push ax
99 mario79 616
    call char_todown
87 mario79 617
    call ansi2uni_char
618
    xchg ah,al
619
    cld
620
    scasw
621
    pop  ax
622
    je    .coincides
99 mario79 623
    call char_toupper
87 mario79 624
    call ansi2uni_char
625
    xchg ah,al
379 serge 626
    cld
87 mario79 627
    sub  edi,2
628
    scasw
629
    jne   .name_not_coincide
630
.coincides:
631
    cmp   [esi],byte '/'  ; разделитель пути, конец имени текущего элемента
632
    je   .done
633
    cmp   [esi],byte 0  ; разделитель пути, конец имени текущего элемента
634
    je   .done
635
    jmp   .loop
636
.name_not_coincide:
637
    pop    edi eax esi
638
    stc
639
    ret
640
.done:
641
; проверка конца файла
642
    cmp   [edi],word 3B00h ; сепаратор конца файла ';'
643
    je   .done_1
644
; проверка для файлов не заканчивающихся сепаратором
645
    movzx   eax,byte [ebp-33]
646
    add   eax,ebp
647
    sub   eax,34
648
    cmp   edi,eax
379 serge 649
    je   .done_1
87 mario79 650
; проверка конца папки
651
    movzx   eax,byte [ebp-1]
652
    add   eax,ebp
653
    cmp   edi,eax
654
    jne   .name_not_coincide
655
.done_1:
656
    pop   edi eax
657
    add   esp,4
658
    inc   esi
659
    clc
660
    ret
379 serge 661
 
87 mario79 662
char_todown:
663
; convert character to uppercase, using cp866 encoding
664
; in: al=symbol
665
; out: al=converted symbol
666
        cmp     al, 'A'
667
        jb      .ret
668
        cmp     al, 'Z'
669
        jbe     .az
670
        cmp     al, 'Ђ'
671
        jb      .ret
672
        cmp     al, 'ђ'
673
        jb      .rus1
674
        cmp     al, 'џ'
675
        ja      .ret
676
; 0x90-0x9F -> 0xE0-0xEF
677
        add     al, 'а'-'ђ'
678
.ret:
679
        ret
680
.rus1:
681
; 0x80-0x8F -> 0xA0-0xAF
682
.az:
683
        add     al, 0x20
684
        ret
379 serge 685
 
87 mario79 686
uni2ansi_char:
687
; convert UNICODE character in al to ANSI character in ax, using cp866 encoding
688
; in: ax=UNICODE character
689
; out: al=converted ANSI character
690
        cmp     ax, 0x80
691
        jb      .ascii
692
        cmp     ax, 0x401
693
        jz      .yo1
694
        cmp     ax, 0x451
695
        jz      .yo2
696
        cmp     ax, 0x410
697
        jb      .unk
698
        cmp     ax, 0x440
699
        jb      .rus1
700
        cmp     ax, 0x450
701
        jb      .rus2
702
.unk:
703
        mov     al, '_'
704
        jmp     .doit
705
.yo1:
706
        mov     al, 'р'
707
        jmp     .doit
708
.yo2:
709
        mov     al, 'с'
710
        jmp     .doit
711
.rus1:
712
; 0x410-0x43F -> 0x80-0xAF
713
        add     al, 0x70
714
        jmp     .doit
715
.rus2:
716
; 0x440-0x44F -> 0xE0-0xEF
717
        add     al, 0xA0
718
.ascii:
719
.doit:
720
        ret