Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
2288 clevermous 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                                     ;;
3
;; Last modify Alexey Teplov  2008. All rights reserved.          ;;
4
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved.        ;;
5
;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa             ;;
6
;; Distributed under terms of the GNU General Public License           ;;
7
;;                                                                     ;;
8
;;  kolibri_ldm.asm the module for Secondary Loader                    ;;
9
;;                                                                     ;;
10
;;  KolibriOS 16-bit loader module,                                    ;;
11
;;                        based on bootcode for KolibriOS              ;;
12
;;                                                                     ;;
13
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
14
 
15
include "lang.inc"
16
 
17
macro _setcursor row,column
18
{
19
        mov     dx, row*256 + column
20
        call    setcursor
21
}
22
long_v_table equ 9   ;long of visible video table
23
size_of_step equ 10
24
d80x25_bottom_num equ 3
25
d80x25_top_num equ 4
26
;It's a module for Secondary Loader to load kolibri OS
27
;
28
start_of_code:
29
        cld
30
; \begin{diamond}[02.12.2005]
31
; if bootloader sets ax = 'KL', then ds:si points to loader block
32
;        cmp     ax, 'KL'
33
;        jnz     @f
34
;        mov     word [cs:cfgmanager.loader_block], si
35
;        mov     word [cs:cfgmanager.loader_block+2], ds
36
;@@:
37
; \end{diamond}[02.12.2005]
38
 
39
; if bootloader sets cx = 'HA' and dx = 'RD', then bx contains identifier of source hard disk
40
; (see comment to bx_from_load)
41
;        cmp     cx, 'HA'
42
;        jnz     no_hd_load
43
;        cmp     dx,'RD'
44
;        jnz     no_hd_load
45
;        mov     word [cs:bx_from_load], bx              ; {SPraid}[13.03.2007]
46
;no_hd_load:
47
 
48
; set up stack
49
        push    cs
50
        pop     ss
51
        xor     ax, ax
52
        mov     sp, ax
53
;        mov     ax, 3000h
54
;        mov     ss, ax
55
;        mov     sp, 0EC00h
56
; set up segment registers
57
        push    cs
58
        pop     ds
59
        push    cs
60
        pop     es
61
 
62
; set videomode
63
        mov     ax, 3
64
        int     0x10
65
 
66
;if lang eq ru
67
 ; Load & set russian VGA font (RU.INC)
68
        mov     bp, RU_FNT1             ; RU_FNT1 - First part
69
        mov     bx, 1000h               ; 768 bytes
70
        mov     cx, 30h                 ; 48 symbols
71
        mov     dx, 80h                 ; 128 - position of first symbol
72
        mov     ax, 1100h
73
        int     10h
74
 
75
        mov     bp, RU_FNT2             ; RU_FNT2 -Second part
76
        mov     bx, 1000h               ; 512 bytes
77
        mov     cx, 20h                 ; 32 symbols
78
        mov     dx, 0E0h                ; 224 - position of first symbol
79
        mov     ax, 1100h
80
        int     10h
81
 ; End set VGA russian font
82
;else if lang eq et
83
;        mov     bp, ET_FNT              ; ET_FNT1
84
;        mov     bx, 1000h               ;
85
;        mov     cx, 255                 ; 256 symbols
86
;        xor     dx, dx                  ; 0 - position of first symbol
87
;        mov     ax, 1100h
88
;        int     10h
89
;end if
90
 
91
; draw frames
92
        push    0xb800
93
        pop     es
94
        xor     di, di
95
        mov     ah, 1*16+15
96
 
97
; draw top
98
        mov     si, d80x25_top
99
        mov     cx, d80x25_top_num * 80
100
@@:
101
        lodsb
102
        stosw
103
        loop    @b
104
; draw spaces
105
        mov     si, space_msg
106
        mov     dx, 25 - d80x25_top_num - d80x25_bottom_num
107
dfl1:
108
        push    si
109
        mov     cx, 80
110
@@:
111
        lodsb
112
        stosw
113
        loop    @b
114
        pop     si
115
        dec     dx
116
        jnz     dfl1
117
; draw bottom
118
        mov     si, d80x25_bottom
119
        mov     cx, d80x25_bottom_num * 80
120
@@:
121
        lodsb
122
        stosw
123
        loop    @b
124
 
125
        mov     byte [space_msg+80], 0    ; now space_msg is null terminated
126
 
127
        _setcursor d80x25_top_num,0
128
 
129
 
130
; TEST FOR 386+
131
 
132
        mov     bx, 0x4000
133
        pushf
134
        pop     ax
135
        mov     dx, ax
136
        xor     ax, bx
137
        push    ax
138
        popf
139
        pushf
140
        pop     ax
141
        and     ax, bx
142
        and     dx, bx
143
        cmp     ax, dx
144
        jnz     cpugood
145
        mov     si, not386
146
sayerr:
147
        call    print
148
        jmp     $
149
     cpugood:
150
 
151
        push    0
152
        popf
153
        sti
154
 
155
; set up esp
156
        movzx   esp, sp
157
 
158
        push    0
159
        pop     es
160
        and     word [es:0x9031], 0
161
; \begin{Mario79}
162
; find HDD IDE DMA PCI device
163
; check for PCI BIOS
164
        mov     ax, 0xB101
165
        int     0x1A
166
        jc      .nopci
167
        cmp     edx, 'PCI '
168
        jnz     .nopci
169
; find PCI class code
170
; class 1 = mass storage
171
; subclass 1 = IDE controller
172
; a) class 1, subclass 1, programming interface 0x80
173
        mov     ax, 0xB103
174
        mov     ecx, 1*10000h + 1*100h + 0x80
175
        xor     si, si  ; device index = 0
176
        int     0x1A
177
        jnc     .found
178
; b) class 1, subclass 1, programming interface 0x8A
179
        mov     ax, 0xB103
180
        mov     ecx, 1*10000h + 1*100h + 0x8A
181
        xor     si, si  ; device index = 0
182
        int     0x1A
183
        jnc     .found
184
; c) class 1, subclass 1, programming interface 0x85
185
        mov     ax, 0xB103
186
        mov     ecx, 1*10000h + 1*100h + 0x85
187
        xor     si, si
188
        int     0x1A
189
        jc      .nopci
190
.found:
191
; get memory base
192
        mov     ax, 0xB10A
193
        mov     di, 0x20        ; memory base is config register at 0x20
194
        int     0x1A
195
        jc      .nopci
196
        and     cx, 0xFFF0      ; clear address decode type
197
        mov     [es:0x9031], cx
198
.nopci:
199
; \end{Mario79}
200
 
201
        mov     al, 0xf6        ; Ñáðîñ êëàâèàòóðû, ðàçðåøèòü ñêàíèðîâàíèå
202
        out     0x60, al
203
        xor     cx, cx
204
wait_loop:       ; variant 2
205
; reading state of port of 8042 controller
206
        in      al, 64h
207
        and     al, 00000010b  ; ready flag
208
; wait until 8042 controller is ready
209
        loopnz  wait_loop
210
 
211
;;;/diamond today   5.02.2008
212
; set keyboard typematic rate & delay
213
        mov     al, 0xf3
214
        out     0x60, al
215
        xor     cx, cx
216
@@:
217
        in      al, 64h
218
        test    al, 2
219
        loopnz  @b
220
        mov     al, 0
221
        out     0x60, al
222
        xor     cx, cx
223
@@:
224
        in      al, 64h
225
        test    al, 2
226
        loopnz  @b
227
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
228
; --------------- APM ---------------------
229
        and     word [es:0x9044], 0     ; ver = 0.0 (APM not found)
230
        mov     ax, 0x5300
231
        xor     bx, bx
232
        int     0x15
233
        jc      apm_end                 ; APM not found
234
        test    cx, 2
235
        jz      apm_end                 ; APM 32-bit protected-mode interface not supported
236
        mov     [es:0x9044], ax         ; Save APM Version
237
        mov     [es:0x9046], cx         ; Save APM flags
238
 
239
        ; Write APM ver ----
240
        and     ax, 0xf0f
241
        add     ax, '00'
242
        mov     si, msg_apm
243
        mov     [si + 5], ah
244
        mov     [si + 7], al
245
        _setcursor 0, 3
246
        call    printplain
247
        ; ------------------
248
 
249
        mov     ax, 0x5304              ; Disconnect interface
250
        xor     bx, bx
251
        int     0x15
252
        mov     ax, 0x5303              ; Connect 32 bit mode interface
253
        xor     bx, bx
254
        int     0x15
255
 
256
        mov     [es:0x9040], ebx
257
        mov     [es:0x9050], ax
258
        mov     [es:0x9052], cx
259
        mov     [es:0x9054], dx
260
 
261
apm_end:
262
        _setcursor d80x25_top_num, 0
263
 
264
;CHECK current of code
265
        cmp     [cfgmanager.loader_block], -1
266
        jz      noloaderblock
267
        les     bx, [cfgmanager.loader_block]
268
        cmp     byte [es:bx], 1
269
        mov     si, loader_block_error
270
        jnz     sayerr
271
        push    0
272
        pop     es
273
 
274
noloaderblock:
275
; DISPLAY VESA INFORMATION
276
        call    print_vesa_info
277
        call    calc_vmodes_table
278
        call    check_first_parm   ;check and enable cursor_pos
279
 
280
 
281
; \begin{diamond}[30.11.2005]
282
cfgmanager:
283
; settings:
284
; a) preboot_graph = graphical mode
285
;    preboot_gprobe = probe this mode?
286
; b) preboot_dma  = use DMA access?
287
; c) preboot_vrrm = use VRR?
288
 
289
; determine default settings
290
        mov     [.bSettingsChanged], 0
291
 
292
;.preboot_gr_end:
293
        mov     di, preboot_device
294
; if image in memory is present and [preboot_device] is uninitialized,
295
; set it to use this preloaded image
296
        cmp     byte [di], 0
297
        jnz     .preboot_device_inited
298
        cmp     [.loader_block], -1
299
        jz      @f
300
        les     bx, [.loader_block]
301
        test    byte [es:bx+1], 1
302
        jz      @f
303
        mov     byte [di], 3
304
        jmp     .preboot_device_inited
305
@@:
306
; otherwise, set [preboot_device] to 1 (default value - boot from floppy)
307
        mov     byte [di], 1
308
.preboot_device_inited:
309
; following 6 lines set variables to 1 if its current value is 0
310
        cmp     byte [di+preboot_dma-preboot_device], 1
311
        adc     byte [di+preboot_dma-preboot_device], 0
312
        cmp     byte [di+preboot_biosdisk-preboot_device], 1
313
        adc     byte [di+preboot_biosdisk-preboot_device], 0
314
        cmp     byte [di+preboot_vrrm-preboot_device], 1
315
        adc     byte [di+preboot_vrrm-preboot_device], 0
316
; notify user
317
        _setcursor 5,2
318
 
319
        mov     si, linef
320
        call    printplain
321
        mov     si, start_msg
322
        call    print
323
        mov     si, time_msg
324
        call    print
325
; get start time
326
        call    .gettime
327
        mov     [.starttime], eax
328
        mov     word [.timer], .newtimer
329
        mov     word [.timer+2], cs
330
.printcfg:
331
        _setcursor 9,0
332
        mov     si, current_cfg_msg
333
        call    print
334
        mov     si, curvideo_msg
335
        call    print
336
 
337
        call    draw_current_vmode
338
 
339
        mov     si, usebd_msg
340
        cmp     [preboot_biosdisk], 1
341
        call    .say_on_off
342
        mov     si, vrrm_msg
343
        cmp     [preboot_vrrm], 1
344
        call    .say_on_off
345
;        mov     si, preboot_device_msg
346
;        call    print
347
;        mov     al, [preboot_device]
348
;        and     eax, 7
349
;        mov     si, [preboot_device_msgs+eax*2]
350
;        call    printplain
351
.show_remarks:
352
; show remarks in gray color
353
        mov     di, ((21-num_remarks)*80 + 2)*2
354
        push    0xB800
355
        pop     es
356
        mov     cx, num_remarks
357
        mov     si, remarks
358
.write_remarks:
359
        lodsw
360
        push    si
361
        xchg    ax, si
362
        mov     ah, 1*16+7      ; background: blue (1), foreground: gray (7)
363
        push    di
364
.write_remark:
365
        lodsb
366
        test    al, al
367
        jz      @f
368
        stosw
369
        jmp     .write_remark
370
@@:
371
        pop     di
372
        pop     si
373
        add     di, 80*2
374
        loop    .write_remarks
375
.wait:
376
        _setcursor 25,0         ; out of screen
377
; set timer interrupt handler
378
        cli
379
        push    0
380
        pop     es
381
        push    dword [es:8*4]
382
        pop     dword [.oldtimer]
383
        push    dword [.timer]
384
        pop     dword [es:8*4]
385
;        mov     eax, [es:8*4]
386
;        mov     [.oldtimer], eax
387
;        mov     eax, [.timer]
388
;        mov     [es:8*4], eax
389
        sti
390
; wait for keypressed
391
        xor     ax, ax
392
        int     16h
393
        push    ax
394
; restore timer interrupt
395
;        push    0
396
;        pop     es
397
        mov     eax, [.oldtimer]
398
        mov     [es:8*4], eax
399
        mov     [.timer], eax
400
        _setcursor 7,0
401
        mov     si, space_msg
402
        call    printplain
403
; clear remarks and restore normal attributes
404
        push    es
405
        mov     di, ((21-num_remarks)*80 + 2)*2
406
        push    0xB800
407
        pop     es
408
        mov     cx, num_remarks
409
        mov     ax, ' ' + (1*16 + 15)*100h
410
@@:
411
        push    cx
412
        mov     cx, 76
413
        rep stosw
414
        pop     cx
415
        add     di, 4*2
416
        loop    @b
417
        pop     es
418
        pop     ax
419
; switch on key
420
        cmp     al, 13
421
        jz      .continue
422
        or      al, 20h
423
        cmp     al, 'a'
424
        jz      .change_a
425
        cmp     al, 'b'
426
        jz      .change_b
427
        cmp     al, 'c'
428
        jnz     .show_remarks
429
 
430
        _setcursor 15,0
431
        mov     si, vrrmprint
432
        call    print
433
        mov     bx, '12'
434
        call    getkey
435
        mov     [preboot_vrrm], al
436
        _setcursor 12,0
437
.d:
438
        mov     [.bSettingsChanged], 1
439
        call    clear_vmodes_table             ;clear vmodes_table
440
        jmp     .printcfg
441
.change_a:
442
.loops:
443
        call    draw_vmodes_table
444
        _setcursor 25,0         ; out of screen
445
        xor     ax, ax
446
        int     0x16
447
;        call    clear_table_cursor             ;clear current position of cursor
448
 
449
        mov     si, word [cursor_pos]
450
 
451
        cmp     ah, 0x48;x,0x48E0               ; up
452
        jne     .down
453
        cmp     si, modes_table
454
        jbe     .loops
455
        sub     word [cursor_pos], size_of_step
456
        jmp     .loops
457
 
458
.down:
459
        cmp     ah, 0x50;x,0x50E0               ; down
460
        jne     .pgup
461
        cmp     word[es:si+10], -1
462
        je      .loops
463
        add     word [cursor_pos], size_of_step
464
        jmp     .loops
465
 
466
.pgup:
467
        cmp     ah, 0x49                ; page up
468
        jne     .pgdn
469
        sub     si, size_of_step*long_v_table
470
        cmp     si, modes_table
471
        jae     @f
472
        mov     si, modes_table
473
@@:
474
        mov     word [cursor_pos], si
475
        mov     si, word [home_cursor]
476
        sub     si, size_of_step*long_v_table
477
        cmp     si, modes_table
478
        jae     @f
479
        mov     si, modes_table
480
@@:
481
        mov     word [home_cursor], si
482
        jmp     .loops
483
 
484
.pgdn:
485
        cmp     ah, 0x51                ; page down
486
        jne     .enter
487
        mov     ax, [end_cursor]
488
        add     si, size_of_step*long_v_table
489
        cmp     si, ax
490
        jb      @f
491
        mov     si, ax
492
        sub     si, size_of_step
493
@@:
494
        mov     word [cursor_pos], si
495
        mov     si, word [home_cursor]
496
        sub     ax, size_of_step*long_v_table
497
        add     si, size_of_step*long_v_table
498
        cmp     si, ax
499
        jb      @f
500
        mov     si, ax
501
@@:
502
        mov     word [home_cursor], si
503
        jmp     .loops
504
 
505
.enter:
506
        cmp     al, 0x0D;x,0x1C0D               ; enter
507
        jne     .loops
508
        push    word [cursor_pos]
509
        pop     bp
510
        push    word [es:bp]
511
        pop     word [x_save]
512
        push    word [es:bp+2]
513
        pop     word [y_save]
514
        push    word [es:bp+6]
515
        pop     word [number_vm]
516
        mov     word [preboot_graph], bp          ;save choose
517
 
518
        jmp     .d
519
 
520
.change_b:
521
        _setcursor 15,0
522
;        mov     si, ask_dma
523
;        call    print
524
;        mov     bx, '13'
525
;        call    getkey
526
;        mov     [preboot_dma], al
527
        mov     si, ask_bd
528
        call    print
529
        mov     bx, '12'
530
        call    getkey
531
        mov     [preboot_biosdisk], al
532
        _setcursor 11,0
533
        jmp     .d
534
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
535
.say_on_off:
536
        pushf
537
        call    print
538
        mov     si, on_msg
539
        popf
540
        jz      @f
541
        mov     si, off_msg
542
@@:
543
        jmp     printplain
544
; novesa and vervesa strings are not used at the moment of executing this code
545
virtual at novesa
546
.oldtimer dd ?
547
.starttime dd ?
548
.bSettingsChanged db ?
549
.timer dd ?
550
end virtual
551
.loader_block dd -1
552
.gettime:
553
        mov     ah, 0
554
        int     1Ah
555
        xchg    ax, cx
556
        shl     eax, 10h
557
        xchg    ax, dx
558
        ret
559
.newtimer:
560
        push    ds
561
        push    cs
562
        pop     ds
563
        pushf
564
        call    [.oldtimer]
565
        pushad
566
        call    .gettime
567
        sub     eax, [.starttime]
568
        sub     ax, 18*5
569
        jae     .timergo
570
        neg     ax
571
        add     ax, 18-1
572
        mov     bx, 18
573
        xor     dx, dx
574
        div     bx
575
if lang eq ru
576
; ¯®¤®¦¤¨â¥ 5 ᥪ㭤, 4/3/2 ᥪ㭤ë, 1 ᥪ㭤ã
577
        cmp     al, 5
578
        mov     cl, ' '
579
        jae     @f
580
        cmp     al, 1
581
        mov     cl, 'ã'
582
        jz      @f
583
        mov     cl, 'ë'
584
@@:
585
        mov     [time_str+9], cl
586
else if lang eq et
587
        cmp     al, 1
588
        ja      @f
589
        mov     [time_str+9], ' '
590
        mov     [time_str+10], ' '
591
@@:
592
else
593
; wait 5/4/3/2 seconds, 1 second
594
        cmp     al, 1
595
        mov     cl, 's'
596
        ja      @f
597
        mov     cl, ' '
598
@@:
599
        mov     [time_str+9], cl
600
end if
601
        add     al, '0'
602
        mov     [time_str+1], al
603
        mov     si, time_msg
604
        _setcursor 7,0
605
        call    print
606
        _setcursor 25,0
607
        popad
608
        pop     ds
609
        iret
610
.timergo:
611
        push    0
612
        pop     es
613
        mov     eax, [.oldtimer]
614
        mov     [es:8*4], eax
615
        mov     sp, 0EC00h
616
.continue:
617
        sti
618
        _setcursor 6,0
619
        mov     si, space_msg
620
        call    printplain
621
        call    printplain
622
        _setcursor 6,0
623
        mov     si, loading_msg
624
        call    print
625
        _setcursor 15,0
626
        cmp     [.bSettingsChanged], 0
627
        jz      .load
628
        cmp     [.loader_block], -1
629
        jz      .load
630
        les     bx, [.loader_block]
631
        mov     eax, [es:bx+3]
632
        push    ds
633
        pop     es
634
        test    eax, eax
635
        jz      .load
636
        push    eax
637
        mov     si, save_quest
638
        call    print
639
.waityn:
640
        mov     ah, 0
641
        int     16h
642
        or      al, 20h
643
        cmp     al, 'n'
644
        jz      .loadc
645
        cmp     al, 'y'
646
        jnz     .waityn
647
        call    putchar
648
        mov     byte [space_msg+80], 186
649
        pop     eax
650
        push    cs
651
        push    .cont
652
        push    eax
653
        retf
654
.loadc:
655
        pop     eax
656
.cont:
657
        push    cs
658
        pop     ds
659
        mov     si, space_msg
660
        mov     byte [si+80], 0
661
        _setcursor 15,0
662
        call    printplain
663
        _setcursor 15,0
664
.load:
665
; \end{diamond}[02.12.2005]
666
 
667
; ASK GRAPHICS MODE
668
 
669
        call    set_vmode
670
 
671
; GRAPHICS ACCELERATION
672
; force yes
673
        mov     [es:0x901C], byte 1
674
 
675
; DMA ACCESS TO HD
676
 
677
        mov     al, [preboot_dma]
678
        mov     [es:0x901F], al
679
 
680
; VRR_M USE
681
 
682
        mov     al, [preboot_vrrm]
683
        mov     [es:0x9030], al
684
        mov     [es:0x901E], byte 1
685
 
686
; BOOT DEVICE
687
 
688
        mov     al, [preboot_device]
689
        dec     al
690
        mov     [boot_dev], al
691
 
692
 
693
 
694
 
695
 
696
 
697
;;;;;;;;;;; set videomode
698
        xor     ax, ax
699
        mov     es, ax
700
 
701
        mov     ax, [es:0x9008]         ; vga & 320x200
702
        mov     bx, ax
703
        cmp     ax, 0x13
704
        je      setgr
705
        cmp     ax, 0x12
706
        je      setgr
707
        mov     ax, 0x4f02              ; Vesa
708
setgr:
709
        int     0x10
710
        test    ah, ah
711
        mov     si, fatalsel
712
        jnz     v_mode_error
713
; set mode 0x12 graphics registers:
714
        cmp     bx, 0x12
715
        jne     gmok2
716
 
717
        mov     al, 0x05
718
        mov     dx, 0x03ce
719
        push    dx
720
        out     dx, al      ; select GDC mode register
721
        mov     al, 0x02
722
        inc     dx
723
        out     dx, al      ; set write mode 2
724
 
725
        mov     al, 0x02
726
        mov     dx, 0x03c4
727
        out     dx, al      ; select VGA sequencer map mask register
728
        mov     al, 0x0f
729
        inc     dx
730
        out     dx, al      ; set mask for all planes 0-3
731
 
732
        mov     al, 0x08
733
        pop     dx
734
        out     dx, al      ; select GDC bit mask register
735
                           ; for writes to 0x03cf
736
gmok2:
737
        push    ds
738
        pop     es
739
 
740
        jmp     $
741
 
742
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
743
;data
744
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
745
include "lang.inc"
746
include "bootstr.inc"     ; language-independent boot messages
747
;if lang eq en
748
;include "booteng.inc"     ; english system boot messages
749
;else if lang eq ru
750
include "bootru.inc"      ; russian system boot messages
751
include "ru.inc"               ; Russian font
752
;else if lang eq et
753
;include "bootet.inc"      ; estonian system boot messages
754
;include "et.inc"              ; Estonian font
755
;else
756
;include "bootge.inc"      ; german system boot messages
757
;end if
758
 
759
include 'macros.inc'
760
include 'bootvesa.inc'
761
 
762
include "preboot.inc"
763
 
764
 
765
setcursor:
766
; in: dl=column, dh=row
767
        mov     ah, 2
768
        mov     bh, 0
769
        int     10h
770
        ret
771
 
772
putchar:
773
; in: al=character
774
        mov     ah, 0Eh
775
        mov     bh, 0
776
        int     10h
777
        ret
778
 
779
print:
780
; in: si->string
781
        mov     al, 186
782
        call    putchar
783
        mov     al, ' '
784
        call    putchar
785
 
786
printplain:
787
; in: si->string
788
        pusha
789
        lodsb
790
@@:
791
        call    putchar
792
        lodsb
793
        cmp     al, 0
794
        jnz     @b
795
        popa
796
        ret
797
 
798
getkey:
799
; get number in range [bl,bh] (bl,bh in ['0'..'9'])
800
; in: bx=range
801
; out: ax=digit (1..9, 10 for 0)
802
        mov     ah, 0
803
        int     16h
804
        cmp     al, bl
805
        jb      getkey
806
        cmp     al, bh
807
        ja      getkey
808
        push    ax
809
        call    putchar
810
        pop     ax
811
        and     ax, 0Fh
812
        jnz     @f
813
        mov     al, 10
814
@@:
815
        ret