; Функции работы с консолью для программ КолибриОС ; diamond, 2006-2008 format MS COFF public EXPORTS section '.flat' code readable align 16 include 'font.inc' include 'conscrl.inc' ;void __stdcall START(dword state); START: ; N.B. The current kernel implementation does not require ; evident heap initialization, because if DLL is loaded, heap is already initialized ; (if heap was not initialized, loader does this implicitly). ; So this action does nothing useful, but nothing harmful. push 68 pop eax push 11 pop ebx int 0x40 or eax, -1 ret 4 ; Инициализация консоли ; void __stdcall con_init(dword wnd_width, dword wnd_height, ; dword scr_width, dword scr_height, const char* title); align 4 con_init: pop eax pop [con.wnd_width] pop [con.wnd_height] pop [con.scr_width] pop [con.scr_height] pop [con.title] push eax mov ecx, 4 mov eax, con.wnd_width mov esi, con.def_wnd_width .1: cmp dword [eax], -1 jnz @f mov ebx, [esi] mov [eax], ebx @@: add eax, 4 add esi, 4 loop .1 push ebx ; allocate memory for console data & bitmap data mov eax, [con.scr_width] mul [con.scr_height] lea ecx, [eax+eax] mov eax, [con.wnd_width] mul [con.wnd_height] imul eax, font_width*font_height mov ebx, eax push ebx ecx add ecx, eax push 68 pop eax push 12 pop ebx int 0x40 pop ecx ebx mov edx, con.nomem_err test eax, eax jz con.fatal mov [con.data], eax push edi mov edi, eax shr ecx, 1 mov ax, 0x0720 rep stosw mov ecx, ebx mov [con.image], edi xor eax, eax rep stosb pop edi and byte [con_flags+1], not 2 ; create console thread push 51 pop eax xor ebx, ebx inc ebx mov ecx, con.thread mov edx, con.stack_top int 0x40 mov edx, con.thread_err test eax, eax js con.fatal mov [con.console_tid], eax pop ebx ret con.fatal: ; output string to debug board and die mov cl, [edx] test cl, cl jz @f push 63 pop eax xor ebx, ebx inc ebx int 0x40 inc edx jmp con.fatal @@: or eax, -1 int 0x40 ; dword __stdcall con_get_flags(void); con_get_flags: mov eax, [con_flags] ret ; dword __stdcall con_set_flags(dword flags); con_set_flags: mov eax, [esp+4] and ah, not 2 xchg eax, [con_flags] ret 4 ; dword __stdcall con_get_font_height(void); con_get_font_height: mov eax, font_height ret ; int __stdcall con_get_cursor_height(void); con_get_cursor_height: mov eax, [con.cursor_height] ret ; int __stdcall con_set_cursor_height(int new_height); con_set_cursor_height: mov eax, [esp+4] cmp eax, font_height jae @f xchg eax, [con.cursor_height] ret 4 @@: mov eax, [con.cursor_height] ret 4 ; void __stdcall con_write_asciiz(const char* string); con_write_asciiz: push ebx esi or ebx, -1 mov esi, [esp+12] call con.write pop esi ebx ret 4 ; void __stdcall con_write_string(const char* string, dword length); con_write_length: push ebx esi mov esi, [esp+12] mov ebx, [esp+16] call con.write pop esi ebx ret 8 ; Каждый символ классифицируется как один из con.printfc.normal = 0 ; нормальный символ con.printfc.percent = 1 ; '%' con.printfc.dot = 2 ; '.' con.printfc.asterisk = 3 ; '*' con.printfc.zero = 4 ; '0' con.printfc.digit = 5 ; ненулевая цифра con.printfc.plus = 6 ; '+' con.printfc.minus = 7 ; '-' con.printfc.sharp = 8 ; '#' con.printfc.space = 9 ; ' ' con.printfc.long = 10 ; 'l' for 'long' con.printfc.short = 11 ; 'h' for 'short' con.printfc.dec = 12 ; 'd' = print decimal con.printfc.oct = 13 ; 'o' = print octal con.printfc.unsigned = 14 ; 'u' = print unsigned decimal con.printfc.hex = 15 ; 'x' = print hexadecimal con.printfc.pointer = 16 ; 'p' = print pointer con.printfc.char = 17 ; 'c' = print char con.printfc.string = 18 ; 's' = print string macro set char,type {store byte con.printfc.#type at con.charcodes + char - ' '} con.charcodes: times 'x'-' '+1 db con.printfc.normal set '%', percent set '.', dot set '*', asterisk set '0', zero set '1', digit set '2', digit set '3', digit set '4', digit set '5', digit set '6', digit set '7', digit set '8', digit set '9', digit set ' ', space set '#', sharp set '+', plus set '-', minus set 'X', hex set 'x', hex set 'c', char set 'd', dec set 'h', short set 'i', dec set 'l', long set 'o', oct set 'p', pointer set 's', string set 'u', unsigned purge set align 4 con.charjump dd con_printf.normal dd con_printf.percent dd con_printf.dot dd con_printf.asterisk dd con_printf.zero dd con_printf.digit dd con_printf.plus dd con_printf.minus dd con_printf.sharp dd con_printf.space dd con_printf.long dd con_printf.short dd con_printf.dec dd con_printf.oct dd con_printf.unsigned dd con_printf.hex dd con_printf.pointer dd con_printf.char dd con_printf.string ; int __cdecl con_printf(const char* format, ...) con_printf: xor eax, eax pushad call con.get_data_ptr lea ebp, [esp+20h+8] mov esi, [ebp-4] sub esp, 64 ; reserve space for buffer .loop: xor eax, eax lodsb test al, al jz .done cmp al, '%' jz .spec_begin .normal: call con.write_char_ex inc dword [esp+64+28] jmp .loop .errspec: .percent: add esp, 12 jmp .normal .spec_begin: xor ebx, ebx ; bl = тип позиции: ; 0 = начало ; 1 = прочитан ведущий 0 в спецификации формата ; 2 = читаем поле ширины ; 3 = читаем поле точности ; 4 = прочитано поле размера аргумента ; 5 = читаем поле типа ; bh = флаги: ; 1 = флаг '#', выводить 0/0x/0X ; 2 = флаг '-', выравнивание влево ; 4 = флаг '0', дополнение нулями ; 8 = флаг 'h', короткий аргумент push -1 ; dword [esp+8] = precision push -1 ; dword [esp+4] = width push 0 ; byte [esp] = флаг 0/'+'/' ' .spec: xor eax, eax lodsb test al, al jz .done cmp al, ' ' jb .normal cmp al, 'x' ja .normal movzx ecx, byte [con.charcodes + eax - ' '] jmp [con.charjump + ecx*4] .sharp: test bl, bl jnz .errspec or bh, 1 jmp .spec .minus: test bl, bl jnz .errspec or bh, 2 jmp .spec .plus: .space: test bl, bl jnz .errspec cmp byte [esp], '+' jz .spec mov byte [esp], al jmp .spec .zero: test bl, bl jnz .digit test bh, 2 jnz .spec or bh, 4 inc ebx jmp .spec .digit: sub al, '0' cmp bl, 2 ja .precision mov bl, 2 xchg eax, [esp+4] test eax, eax js .spec lea eax, [eax*5] add eax, eax add [esp+4], eax jmp .spec .precision: cmp bl, 3 jnz .errspec xchg eax, [esp+8] lea eax, [eax*5] add eax, eax add [esp+8], eax jmp .spec .asterisk: mov eax, [ebp] add ebp, 4 cmp bl, 2 ja .asterisk_precision test eax, eax jns @f neg eax or bh, 2 @@: mov [esp+4], eax mov bl, 3 jmp .spec .asterisk_precision: cmp bl, 3 jnz .errspec mov [esp+8], eax inc ebx jmp .spec .dot: cmp bl, 2 ja .errspec mov bl, 3 and dword [esp+8], 0 jmp .spec .long: cmp bl, 3 ja .errspec mov bl, 4 jmp .spec .short: cmp bl, 3 ja .errspec mov bl, 4 or bh, 8 jmp .spec .unsigned: .dec: push 10 jmp .write_number .pointer: mov dword [esp+12], 8 or bh, 4 and bh, not 8 .hex: push 16 jmp @f .oct: push 8 @@: mov byte [esp+4], 0 .write_number: pop ecx push edi lea edi, [esp+16+64-1] ; edi -> end of buffer mov byte [edi], 0 push edx push eax mov eax, [ebp] add ebp, 4 test bh, 8 jz @f movzx eax, ax cmp byte [esp], 'd' jnz @f movsx eax, ax @@: xor edx, edx test eax, eax jns @f cmp byte [esp], 'd' jnz @f inc edx neg eax @@: push edx xor edx, edx ; число в eax, основание системы счисления в ecx @@: cmp dword [esp+16+8], 0 jnz .print_num test eax, eax jz .zeronum .print_num: div ecx xchg eax, edx cmp al, 10 sbb al, 69h das cmp byte [esp+4], 'x' jnz @f or al, 20h @@: dec edi mov [edi], al xor eax, eax xchg eax, edx test eax, eax jnz .print_num .zeronum: push 0 mov edx, [esp+12] lea eax, [esp+32+64-1] sub eax, edi cmp dword [esp+20+8], -1 jz .noprec1 cmp eax, [esp+20+8] jae .len_found1 mov eax, [esp+20+8] jmp .len_found1 .noprec1: test bh, 4 jnz .do_print_num .len_found1: test bh, 2 jnz .do_print_num cmp byte [esp+20], 0 jz @f inc eax @@: cmp byte [esp+20], 0 jnz @f cmp byte [esp+4], 0 jz @f inc eax @@: test bh, 1 jz .nosharp1 cmp cl, 8 jnz @f inc eax jmp .nosharp1 @@: cmp cl, 16 jnz .nosharp1 inc eax inc eax .nosharp1: cmp dword [esp+20+4], -1 jz .do_print_num sub eax, [esp+20+4] jae .do_print_num push ecx mov ecx, eax mov al, ' ' @@: xchg edi, [esp+20] call con.write_char_ex inc dword [esp+24+12+64+28] xchg edi, [esp+20] inc dword [esp+4] inc ecx jnz @b pop ecx .do_print_num: mov al, '-' cmp byte [esp+4], 0 jnz .write_sign mov al, [esp+20] test al, al jz .sign_written .write_sign: call .num_write_char .sign_written: test bh, 1 jz .nosharp2 mov al, '0' cmp cl, 8 jz @f cmp cl, 16 jnz .nosharp2 call .num_write_char mov al, [esp+8] @@: call .num_write_char .nosharp2: lea ecx, [esp+32+64-1] sub ecx, edi cmp dword [esp+20+8], -1 jz .noprec2 sub ecx, [esp+20+8] jmp .lead_zeroes .noprec2: test bh, 4 jz .do_print_num2 add ecx, [esp] sub ecx, [esp+20+4] .lead_zeroes: jae .do_print_num2 @@: mov al, '0' call .num_write_char inc ecx jnz @b .do_print_num2: mov al, [edi] test al, al jz .num_written call .num_write_char inc edi jmp .do_print_num2 .num_written: pop ecx mov edi, [esp+12] cmp dword [esp+16+4], -1 jz .num_written2 @@: cmp ecx, [esp+16+4] jae .num_written2 mov al, ' ' call con.write_char inc ecx jmp @b .num_written2: add esp, 16 .spec_done: add esp, 12 jmp .loop .char: mov ecx, [esp+4] cmp ecx, -1 jnz @f inc ecx @@: test ecx, ecx jnz @f inc ecx @@: test bh, 2 jnz .char_left_pad mov al, ' ' dec ecx jz .nowidth add [esp+12+64+28], ecx @@: call con.write_char loop @b .nowidth: mov al, [ebp] add ebp, 4 jmp .percent .char_left_pad: mov al, [ebp] add ebp, 4 call con.write_char_ex add [esp+12+64+28], ecx dec ecx jz .nowidth2 mov al, ' ' @@: call con.write_char loop @b .nowidth2: jmp .spec_done .string: push esi mov esi, [ebp] test esi, esi jnz @f mov esi, con.aNull @@: add ebp, 4 or ecx, -1 @@: inc ecx cmp byte [esi+ecx], 0 jnz @b cmp ecx, [esp+12] jb @f mov ecx, [esp+12] @@: test bh, 2 jnz .write_string cmp dword [esp+8], -1 jz .write_string push ecx sub ecx, [esp+12] jae .nospace mov al, ' ' @@: call con.write_char inc dword [esp+20+64+28] inc ecx jnz @b .nospace: pop ecx .write_string: jecxz .string_written add dword [esp+16+64+28], ecx push ecx @@: lodsb call con.write_char_ex loop @b pop ecx .string_written: pop esi test bh, 2 jz .spec_done cmp dword [esp+4], -1 jz .spec_done sub ecx, [esp+4] jae .spec_done mov al, ' ' @@: call con.write_char inc dword [esp+12+64+28] inc ecx jnz @b jmp .spec_done .done: add esp, 64 popad jmp con.update_screen .num_write_char: xchg edi, [esp+20] call con.write_char_ex inc dword [esp+24+12+64+28] xchg edi, [esp+20] inc dword [esp+4] ret con.write: ; esi = string, ebx = length (ebx=-1 for ASCIIZ strings) push edi call con.get_data_ptr test ebx, ebx jz .done .loop: lodsb cmp ebx, -1 jnz @f test al, al jz .done @@: call con.write_char_ex .next: cmp ebx, -1 jz .loop dec ebx jnz .loop .done: pop edi jmp con.update_screen con.get_data_ptr: mov edi, [con.cur_y] imul edi, [con.scr_width] add edi, [con.cur_x] add edi, edi add edi, [con.data] ret con.write_char_ex: test byte [con_flags+1], 1 jz con.write_special_char con.write_char: push eax stosb mov al, byte [con_flags] stosb mov eax, [con.cur_x] inc eax mov [con.cur_x], eax cmp eax, [con.scr_width] jb @f and [con.cur_x], 0 call con.newline @@: pop eax ret con.write_special_char: cmp [con_esc], 0 jnz .esc_mode .normal_mode: cmp al, 10 jz .write_lf cmp al, 13 jz .write_cr cmp al, 27 jz .write_esc cmp al, 8 jz .write_bs cmp al, 9 jnz con.write_char .write_tab: mov al, ' ' call con.write_char test [con.cur_x], 7 jnz .write_tab ret .write_cr: and [con.cur_x], 0 jmp con.get_data_ptr .write_lf: and [con.cur_x], 0 jmp con.newline .write_bs: cmp [con.cur_x], 0 jz @f dec [con.cur_x] dec edi dec edi ret @@: push eax mov eax, [con.cur_y] dec eax js @f mov [con.cur_y], eax mov eax, [con.scr_width] dec eax mov [con.cur_x], eax dec edi dec edi @@: pop eax ret .write_esc: mov [con_esc], 1 mov [con_esc_attr_n], 1 and [con_esc_attrs], 0 ret .esc_mode: cmp [con_sci], 0 jnz .esc_sci cmp al, '[' jnz @f mov [con_sci], 1 ret @@: push eax mov al, 27 call con.write_char pop eax jmp con.write_char .esc_sci: ; this is real Esc sequence cmp al, ';' jz .next_arg cmp al, '0' jb .not_digit cmp al, '9' ja .not_digit push eax ecx edx sub al, '0' movzx eax, al mov ecx, [con_esc_attr_n] mov edx, [con_esc_attrs+(ecx-1)*4] lea edx, [edx*5] lea edx, [edx*2+eax] mov [con_esc_attrs+(ecx-1)*4], edx pop edx ecx eax ret .next_arg: push eax mov eax, [con_esc_attr_n] inc eax cmp al, 4 jbe @f dec eax @@: mov [con_esc_attr_n], eax and [con_esc_attrs+(eax-1)*4], 0 pop eax ret .not_digit: mov [con_esc], 0 mov [con_sci], 0 ; in any case, leave Esc mode cmp al, 'J' jz .cls cmp al, 'H' jz .setcursor cmp al, 'f' jz .setcursor cmp al, 'm' jz .set_attr cmp al, 'A' jz .cursor_up cmp al, 'B' jz .cursor_down cmp al, 'C' jz .cursor_right cmp al, 'D' jz .cursor_left ret ; simply skip unknown sequences .cls: push ecx and [con.cur_x], 0 and [con.cur_y], 0 mov edi, [con.data] push edi mov ecx, [con.scr_width] imul ecx, [con.scr_height] mov ax, 0720h rep stosw pop edi ecx .nosetcursor: ret .setcursor: cmp [con_esc_attr_n], 2 jnz .nosetcursor mov eax, [con_esc_attrs] cmp eax, [con.scr_width] jae @f mov [con.cur_x], eax @@: mov eax, [con_esc_attrs+4] cmp eax, [con.scr_height+4] jae @f mov [con.cur_y], eax .j_get_data: jmp con.get_data_ptr .cursor_up: cmp [con_esc_attr_n], 1 jnz .nosetcursor mov eax, [con.cur_y] sub eax, [con_esc_attrs] jnc @f xor eax, eax @@: mov [con.cur_y], eax jmp .j_get_data .cursor_down: cmp [con_esc_attr_n], 1 jnz .nosetcursor mov eax, [con.cur_y] add eax, [con_esc_attrs] cmp eax, [con.scr_height] jb @f mov eax, [con.scr_height] dec eax @@: mov [con.cur_y], eax jmp .j_get_data .cursor_right: cmp [con_esc_attr_n], 1 jnz .nosetcursor mov eax, [con.cur_x] add eax, [con_esc_attrs] cmp eax, [con.scr_width] jb @f mov eax, [con.scr_width] dec eax @@: mov [con.cur_x], eax jmp .j_get_data .cursor_left: cmp [con_esc_attr_n], 1 jnz .nosetcursor mov eax, [con.cur_x] sub eax, [con_esc_attrs] jnc @f xor eax, eax @@: mov [con.cur_x], eax jmp .j_get_data .set_attr: push eax ecx edx xor ecx, ecx .set_one_attr: mov eax, [con_esc_attrs+ecx*4] cmp al, 0 jz .attr_normal cmp al, 1 jz .attr_bold cmp al, 5 jz .attr_bgr_bold cmp al, 7 jz .attr_reversed xor edx, edx cmp al, 30 jz .attr_color mov dl, 4 cmp al, 31 jz .attr_color mov dl, 2 cmp al, 32 jz .attr_color mov dl, 6 cmp al, 33 jz .attr_color mov dl, 1 cmp al, 34 jz .attr_color mov dl, 5 cmp al, 35 jz .attr_color mov dl, 3 cmp al, 36 jz .attr_color mov dl, 7 cmp al, 37 jz .attr_color xor edx, edx cmp al, 40 jz .attr_bgr_color mov dl, 0x40 cmp al, 41 jz .attr_bgr_color mov dl, 0x20 cmp al, 42 jz .attr_bgr_color mov dl, 0x60 cmp al, 43 jz .attr_bgr_color mov dl, 0x10 cmp al, 44 jz .attr_bgr_color mov dl, 0x50 cmp al, 45 jz .attr_bgr_color mov dl, 0x30 cmp al, 46 jz .attr_bgr_color mov dl, 0x70 cmp al, 47 jnz .attr_continue .attr_bgr_color: mov eax, [con_flags] and al, 0x8F or al, dl mov [con_flags], eax jmp .attr_continue .attr_color: mov eax, [con_flags] and al, 0xF8 or al, dl mov [con_flags], eax jmp .attr_continue .attr_normal: mov byte [con_flags], 7 jmp .attr_continue .attr_reversed: mov byte [con_flags], 0x70 jmp .attr_continue .attr_bold: or byte [con_flags], 8 jmp .attr_continue .attr_bgr_bold: or byte [con_flags], 0x80 .attr_continue: inc ecx cmp ecx, [con_esc_attr_n] jb .set_one_attr pop edx ecx eax ret con.newline: mov eax, [con.cur_y] inc eax mov [con.cur_y], eax cmp eax, [con.scr_height] jb @f call con.scr_scroll_up @@: call con.get_data_ptr ret con.scr_scroll_up: pushad mov edi, [con.data] mov esi, edi add esi, [con.scr_width] add esi, [con.scr_width] dec [con.cur_y] mov ecx, [con.scr_height] dec ecx imul ecx, [con.scr_width] shr ecx, 1 rep movsd adc ecx, ecx rep movsw mov ax, 0x0720 mov ecx, [con.scr_width] rep stosw popad ret con.data2image: pushad mov edi, [con.image] mov esi, [con.data] mov eax, [con.wnd_ypos] mul [con.scr_width] add eax, [con.wnd_xpos] lea esi, [esi+eax*2] mov ecx, [con.wnd_height] .lh: push ecx mov ecx, [con.wnd_width] .lw: push ecx edi xor eax, eax mov al, [esi+1] push eax and al, 0xF mov ebx, eax ; цвет текста pop eax shr al, 4 mov ebp, eax ; цвет фона sub ebx, ebp lodsb inc esi if font_width > 8 lea edx, [eax+eax+font] else lea edx, [eax+font] end if .sh: mov ecx, [edx] repeat font_width shr ecx, 1 sbb eax, eax and eax, ebx add eax, ebp mov [edi+%-1], al end repeat mov eax, [con.wnd_width] ; imul eax, font_width ; add edi, eax if font_width = 6 lea eax, [eax*2+eax] lea edi, [edi+eax*2] else if font_width = 7 lea edi, [edi+eax*8] sub edi, eax else if font_width = 8 lea edi, [edi+eax*8] else if font_width = 9 lea edi, [edi+eax*8] add edi, eax else if font_width = 10 lea eax, [eax*4+eax] lea edi, [edi+eax*2] else Unknown font_width value! end if if font_width > 8 add edx, 256*2 cmp edx, font+256*2*font_height else add edx, 256 cmp edx, font+256*font_height end if jb .sh pop edi ecx add edi, font_width sub ecx, 1 jnz .lw mov eax, [con.wnd_width] imul eax, (font_height-1)*font_width add edi, eax pop ecx mov eax, [con.scr_width] sub eax, [con.wnd_width] lea esi, [esi+eax*2] dec ecx jnz .lh mov eax, [con.cur_y] sub eax, [con.wnd_ypos] jb .nocursor cmp eax, [con.wnd_height] jae .nocursor inc eax mul [con.wnd_width] imul eax, font_height*font_width mov edx, [con.cur_x] sub edx, [con.wnd_xpos] jb .nocursor cmp edx, [con.wnd_width] jae .nocursor inc edx imul edx, font_width add eax, edx add eax, [con.image] mov edx, [con.wnd_width] imul edx, font_width neg edx mov ecx, [con.cursor_height] jecxz .nocursor .cursor_loop: push ecx mov ecx, font_width add eax, edx push eax @@: xor byte [eax-1], 7 dec eax loop @b pop eax pop ecx loop .cursor_loop .nocursor: popad ret con_exit: cmp byte [esp+4], 0 jz .noexit mov [con.thread_op], 1 call con.wake ret 4 .noexit: push esi mov esi, [con.title] mov edx, con.finished_title mov ecx, 255 call .strcpy mov esi, con.aFinished call .strcpy mov byte [edx], 0 pop esi and [con.cursor_height], 0 push con.finished_title call con_set_title ret 4 .strcpy: jecxz .ret @@: lodsb test al, al jz .ret mov [edx], al inc edx loop @b .ret: ret con_set_title: mov eax, [esp+4] mov [con.title], eax mov [con.thread_op], 2 call con.wake ret 4 ; int __stdcall con_kbhit(void); con_kbhit: test byte [con_flags+1], 2 jnz @f mov eax, [con.input_start] cmp eax, [con.input_end] @@: setnz al movzx eax, al ret con.force_entered_char: cmp [con.entered_char], -1 jnz .ret mov [con.thread_op], 4 call con.wake test byte [con_flags+1], 2 jnz .ret ; wait for response push ebx push 5 pop eax push 2 pop ebx @@: int 0x40 cmp [con.entered_char], -1 jz @b pop ebx .ret: ret ; int __stdcall con_getch(void); con_getch: call con.force_entered_char test byte [con_flags+1], 2 jnz con_getch_closed movzx eax, byte [con.entered_char] sar [con.entered_char], 8 mov byte [con.entered_char+1], 0xFF test al, al jz @f mov byte [con.entered_char], 0xFF @@: ret con_getch_closed: or eax, -1 ret ; int __stdcall con_getch2(void); con_getch2: call con.force_entered_char test byte [con_flags+1], 2 jnz con_getch_closed mov eax, 0xFFFF xchg ax, [con.entered_char] ret ; char* __stdcall con_gets(char* str, int n); con_gets: pop eax push 0 push eax ; char* __stdcall con_gets2(con_gets2_callback callback, char* str, int n); con_gets2: mov eax, [esp+8] ; str pushad mov esi, eax ; str mov ebx, [esp+20h+12] ; n sub ebx, 1 jle .ret mov byte [esi], 0 xor ecx, ecx ; длина уже введённой строки call con.get_data_ptr .loop: call con_getch2 cmp eax, -1 jz .closed test al, al jz .extended cmp al, 8 jz .backspace cmp al, 27 jz .esc cmp al, 13 jz .enter cmp al, 9 jz .tab inc ecx mov dl, al call con.write_char_ex push [con.cur_x] push [con.cur_y] push edi push esi @@: lodsb mov [esi-1], dl mov dl, al test al, al jz @f call con.write_char_ex jmp @b @@: mov [esi], dl pop esi inc esi pop edi pop [con.cur_y] pop [con.cur_x] .update_screen_and_loop: call con.update_screen cmp ecx, ebx jb .loop .ret_us: mov edx, [con.cur_x] @@: lodsb test al, al jz @f inc edx cmp edx, [con.scr_width] jb @b xor edx, edx call con.newline jmp @b @@: mov [con.cur_x], edx call con.get_data_ptr call con.update_screen jmp .ret .esc: mov edx, [con.cur_x] @@: lodsb test al, al jz @f inc edx cmp edx, [con.scr_width] jb @b xor edx, edx call con.newline jmp @b @@: mov [con.cur_x], edx call con.get_data_ptr dec esi xor ecx, ecx @@: mov byte [esi], 0 cmp esi, [esp+20h+8] jbe .update_screen_and_loop mov al, 8 call con.write_special_char mov al, ' ' call con.write_char mov al, 8 call con.write_special_char dec esi jmp @b .delete: cmp byte [esi], 0 jz .loop lodsb call con.write_char_ex .backspace: cmp esi, [esp+20h+8] jbe .loop push esi mov edx, [con.cur_x] @@: lodsb test al, al jz @f inc edx cmp edx, [con.scr_width] jb @b xor edx, edx call con.newline jmp @b @@: mov [con.cur_x], edx call con.get_data_ptr dec esi mov al, 8 call con.write_special_char mov al, ' ' call con.write_char mov al, 8 call con.write_special_char mov dl, 0 @@: cmp esi, [esp] jbe @f mov al, 8 call con.write_special_char dec esi xchg dl, [esi] mov al, dl call con.write_char mov al, 8 call con.write_special_char jmp @b @@: pop esi dec esi mov [esi], dl dec ecx jmp .update_screen_and_loop .enter: mov edx, [con.cur_x] @@: lodsb test al, al jz @f inc edx cmp edx, [con.scr_width] jb @b xor edx, edx call con.newline jmp @b @@: mov [con.cur_x], edx call con.get_data_ptr mov al, 10 mov [esi-1], al mov byte [esi], 0 call con.write_special_char call con.update_screen jmp .ret .tab: mov al, 0 mov ah, 0xF .extended: xchg al, ah cmp al, 0x4B jz .left cmp al, 0x4D jz .right cmp al, 0x47 jz .home cmp al, 0x4F jz .end cmp al, 0x53 jz .delete ; give control to callback function cmp dword [esp+20h+4], 0 jz .loop ; remember length of text before and length of text after ; and advance cursor to the end of line push ecx push eax lea edx, [esi+1] @@: lodsb test al, al jz @f call con.write_char_ex jmp @b @@: sub esi, edx pop eax push esi dec edx sub edx, [esp+28h+8] push edx push esp ; ppos mov ecx, [esp+30h+4] lea edx, [esp+30h+12] push edx ; pn lea edx, [esp+34h+8] push edx ; pstr push eax ; keycode call ecx call con.get_data_ptr dec eax js .callback_nochange jz .callback_del dec eax jz .callback_output ; callback returned 2 - exit add esp, 12 jmp .ret .callback_nochange: ; callback returned 0 - string was not changed, only restore cursor position pop esi pop ecx test ecx, ecx jz .cncs @@: mov al, 8 call con.write_special_char loop @b .cncs: pop ecx add esi, [esp+20h+8] jmp .callback_done .callback_del: ; callback returned 1 - string was changed, delete old string and output new mov ecx, [esp+8] test ecx, ecx jz .cds @@: mov al, 8 call con.write_special_char mov al, ' ' call con.write_char_ex mov al, 8 call con.write_special_char loop @b .cds: .callback_output: ; callback returned 2 - string was changed, output new string pop edx pop esi pop ecx mov esi, [esp+20h+8] xor ecx, ecx @@: lodsb test al, al jz @f call con.write_char_ex inc ecx jmp @b @@: dec esi push ecx sub ecx, edx jz .cos @@: mov al, 8 call con.write_special_char dec esi loop @b .cos: pop ecx .callback_done: call con.update_screen mov ebx, [esp+20h+12] dec ebx cmp ecx, ebx jae .ret_us jmp .loop .left: cmp esi, [esp+20h+8] jbe .loop dec esi mov al, 8 call con.write_special_char jmp .update_screen_and_loop .right: cmp byte [esi], 0 jz .loop lodsb call con.write_char_ex jmp .update_screen_and_loop .home: cmp esi, [esp+20h+8] jz .update_screen_and_loop dec esi mov al, 8 call con.write_special_char jmp .home .end: lodsb test al, al jz @f call con.write_char_ex jmp .end @@: dec esi jmp .update_screen_and_loop .closed: and dword [esp+1Ch], 0 .ret: popad ret 12 ; void __stdcall con_cls(); con_cls: push edi call con.write_special_char.cls pop edi call con.update_screen ret ; void __stdcall con_get_cursor_pos(int* px, int* py); con_get_cursor_pos: push eax ecx mov eax, [esp+12] mov ecx, [con.cur_x] mov [eax], ecx mov eax, [esp+16] mov ecx, [con.cur_y] mov [eax], ecx pop ecx eax ret 8 ; void __stdcall con_set_cursor_pos(int px, int py); con_set_cursor_pos: push eax mov eax, [esp+8] cmp eax, [con.scr_width] jae @f mov [con.cur_x], eax @@: mov eax, [esp+12] cmp eax, [con.scr_height] jae @f mov [con.cur_y], eax @@: pop eax call con.update_screen ret 8 con.update_screen: push eax mov eax, [con.cur_y] sub eax, [con.wnd_ypos] jb .up cmp eax, [con.wnd_height] jb .done mov eax, [con.cur_y] sub eax, [con.wnd_height] inc eax jmp .set .up: mov eax, [con.cur_y] .set: mov [con.wnd_ypos], eax .done: pop eax mov [con.thread_op], 3 con.wake: pushad mov al, [con.thread_op] cmp al, byte [con.ipc_buf+0x10] jz .ret @@: push 60 pop eax push 2 pop ebx mov ecx, [con.console_tid] jecxz .ret mov edx, con.thread_op push 1 pop esi int 0x40 test eax, eax jz @f push 5 pop eax mov bl, 1 int 0x40 jmp @b @@: .ret: popad ret ; Поток окна консоли. Обрабатывает ввод и вывод. con.thread: ; Поток реагирует на IPC, которое используется только для того, чтобы его можно было "разбудить" push 40 pop eax push 0x67 pop ebx int 0x40 mov al, 60 mov bl, 1 mov ecx, con.ipc_buf push 0x11 pop edx int 0x40 mov al, 66 mov bl, 1 mov ecx, ebx int 0x40 con.redraw: call con.draw_window con.msg_loop: cmp dword [con.bUpPressed], 0 jnz .wait_timeout push 10 pop eax jmp @f .wait_timeout: push 23 pop eax push 5 pop ebx @@: int 0x40 dec eax jz con.redraw dec eax jz con.key dec eax jz con.button cmp al, 4 jz con.ipc jmp con.mouse con.button: ; we have only one button, close con.thread_exit: or byte [con_flags+1], 2 and [con.console_tid], 0 or eax, -1 int 0x40 con.key: mov al, 2 int 0x40 ; ah = scancode cmp ah, 0xE0 jnz @f mov [con.bWasE0], 1 jmp con.msg_loop @@: shr eax, 8 xchg ah, [con.bWasE0] test al, al jle con.msg_loop cmp al, 0x1D jz con.msg_loop cmp al, 0x2A jz con.msg_loop cmp al, 0x36 jz con.msg_loop cmp al, 0x38 jz con.msg_loop cmp al, 0x3A jz con.msg_loop cmp al, 0x45 jz con.msg_loop cmp al, 0x46 jz con.msg_loop mov edx, eax push 66 pop eax push 3 pop ebx int 0x40 ; eax = control key state test dh, dh jnz .extended bt [scan_has_ascii], edx jnc .extended test al, 0x30 jnz .extended ; key has ASCII code push eax edx push 2 pop ecx test al, 3 jnz @f dec ecx @@: push 26 pop eax mov bl, 2 mov edx, con.kbd_layout int 0x40 pop edx eax mov dh, [con.kbd_layout+edx] test al, 0xC jz @f sub dh, 0x60 jmp @f .extended: mov dh, 0 ; no ASCII code @@: ; dh contains ASCII-code; now convert scancode to extended key code mov ecx, con.extended_alt test al, 0x30 jnz .xlat mov ecx, con.extended_shift test al, 3 jnz .xlat mov ecx, con.extended_ctrl test al, 0xC jnz .xlat xchg dl, dh cmp dh, 0x57 jz @f cmp dh, 0x58 jnz .gotcode @@: add dh, 0x85-0x57 jmp .gotcode .xlat: movzx eax, dl mov dl, dh mov dh, [eax+ecx] .gotcode: test dh, dh jz con.msg_loop cmp dh, 0x94 jnz @f mov dl, 0 @@: ; dx contains full keycode cmp [con.bGetchRequested], 0 jz @f mov [con.entered_char], dx jmp con.msg_loop @@: mov eax, [con.input_end] mov ecx, eax add eax, 2 cmp eax, con.input_buffer_end jnz @f mov eax, con.input_buffer @@: cmp eax, [con.input_start] jnz @f ; buffer overflow, make beep and continue push 55 pop eax mov ebx, eax mov esi, con.beep int 0x40 jmp con.msg_loop @@: mov [ecx], dx mov [con.input_end], eax jmp con.msg_loop con.ipc: movzx eax, byte [con.ipc_buf+0x10] mov byte [con.ipc_buf+4], 8 mov byte [con.ipc_buf+0x10], 0 dec eax jz con.thread_exit dec eax jz con.set_title dec eax jz con.redraw_image dec eax jz con.getch jmp con.msg_loop con.set_title: push 71 pop eax push 1 pop ebx mov ecx, [con.title] int 0x40 jmp con.msg_loop con.redraw_image: call con.data2image call con.draw_image jmp con.msg_loop con.getch: mov eax, [con.input_start] cmp eax, [con.input_end] jz .noinput mov ecx, [eax] mov [con.entered_char], cx inc eax inc eax cmp eax, con.input_buffer_end jnz @f mov eax, con.input_buffer @@: mov [con.input_start], eax jmp con.msg_loop .noinput: mov [con.bGetchRequested], 1 jmp con.msg_loop con.mouse: xor eax, eax xchg eax, dword [con.bUpPressed] mov dword [con.bUpPressed_saved], eax push 37 pop eax push 2 pop ebx int 0x40 test al, 1 jnz @f cmp [con.vscroll_pt], -1 jz .redraw_if_needed or [con.vscroll_pt], -1 .redraw_if_needed: cmp dword [con.bUpPressed_saved], 0 jnz con.redraw_image jmp con.msg_loop @@: mov al, 37 dec ebx int 0x40 movsx ebx, ax sar eax, 16 cmp [con.vscroll_pt], -1 jnz .vscrolling test ebx, ebx js .redraw_if_needed sub ax, [con.data_width] jb .redraw_if_needed cmp eax, con.vscroll_width jae .redraw_if_needed cmp ebx, con.vscroll_btn_height jb .up sub bx, [con.data_height] jae .redraw_if_needed cmp bx, -con.vscroll_btn_height jge .down add bx, [con.data_height] sub bx, word [con.vscrollbar_pos] jl .vscroll_up cmp bx, word [con.vscrollbar_size] jl .vscroll .vscroll_down: cmp [con.bScrollingDown_saved], 0 jz .vscroll_down_first cmp [con.bScrollingDown_saved], 1 jz .vscroll_down_wasfirst mov [con.bScrollingDown], 2 .vscroll_down_do: mov eax, [con.wnd_ypos] add eax, [con.wnd_height] dec eax mov ebx, [con.scr_height] sub ebx, [con.wnd_height] cmp eax, ebx jb @f mov eax, ebx @@: mov [con.wnd_ypos], eax jmp con.redraw_image .vscroll_down_first: push 26 pop eax push 9 pop ebx int 0x40 mov [con.scroll_down_first_time], eax mov [con.bScrollingDown], 1 jmp .vscroll_down_do .vscroll_down_wasfirst: push 26 pop eax push 9 pop ebx int 0x40 sub eax, [con.scroll_down_first_time] cmp eax, 25 jb @f mov [con.bScrollingDown], 2 jmp .vscroll_down_do @@: mov [con.bScrollingDown], 1 jmp con.msg_loop .vscroll_up: cmp [con.bScrollingUp_saved], 0 jz .vscroll_up_first cmp [con.bScrollingUp_saved], 1 jz .vscroll_up_wasfirst mov [con.bScrollingUp], 2 .vscroll_up_do: mov eax, [con.wnd_ypos] inc eax sub eax, [con.wnd_height] jns @f xor eax, eax @@: mov [con.wnd_ypos], eax jmp con.redraw_image .vscroll_up_first: push 26 pop eax push 9 pop ebx int 0x40 mov [con.scroll_up_first_time], eax mov [con.bScrollingUp], 1 jmp .vscroll_up_do .vscroll_up_wasfirst: push 26 pop eax push 9 pop ebx int 0x40 sub eax, [con.scroll_up_first_time] cmp eax, 25 jb @f mov [con.bScrollingUp], 2 jmp .vscroll_up_do @@: mov [con.bScrollingUp], 1 jmp con.msg_loop .up: cmp [con.bUpPressed_saved], 0 jz .up_first cmp [con.bUpPressed_saved], 1 jz .up_wasfirst mov [con.bUpPressed], 2 .up_do: mov eax, [con.wnd_ypos] dec eax js @f mov [con.wnd_ypos], eax @@: jmp con.redraw_image .up_first: push 26 pop eax push 9 pop ebx int 0x40 mov [con.up_first_time], eax mov [con.bUpPressed], 1 jmp .up_do .up_wasfirst: push 26 pop eax push 9 pop ebx int 0x40 sub eax, [con.up_first_time] cmp eax, 25 jb @f mov [con.bUpPressed], 2 jmp .up_do @@: mov [con.bUpPressed], 1 jmp con.msg_loop .down: cmp [con.bDownPressed_saved], 0 jz .down_first cmp [con.bDownPressed_saved], 1 jz .down_wasfirst mov [con.bDownPressed], 2 .down_do: mov eax, [con.scr_height] sub eax, [con.wnd_height] jbe con.redraw_image cmp [con.wnd_ypos], eax jae con.redraw_image inc [con.wnd_ypos] jmp con.redraw_image .down_first: push 26 pop eax push 9 pop ebx int 0x40 mov [con.down_first_time], eax mov [con.bDownPressed], 1 jmp .down_do .down_wasfirst: push 26 pop eax push 9 pop ebx int 0x40 sub eax, [con.down_first_time] cmp eax, 25 jb @f mov [con.bDownPressed], 2 jmp .down_do @@: mov [con.bDownPressed], 1 jmp con.msg_loop .vscroll: mov [con.vscroll_pt], ebx call con.draw_image jmp con.msg_loop .vscrolling: sub ebx, [con.vscroll_pt] sub ebx, con.vscroll_btn_height jge @f xor ebx, ebx @@: movzx eax, [con.data_height] sub eax, 2*con.vscroll_btn_height sub eax, [con.vscrollbar_size] cmp ebx, eax jb @f lea ebx, [eax-1] @@: xchg eax, ebx mov edx, [con.scr_height] sub edx, [con.wnd_height] inc edx mul edx div ebx cmp [con.wnd_ypos], eax jz con.msg_loop mov [con.wnd_ypos], eax jmp con.redraw_image con.draw_window: push 12 pop eax xor ebx, ebx inc ebx int 0x40 mov al, 48 mov bl, 4 int 0x40 mov ebx, [con.def_wnd_x-2] mov bx, word [con.wnd_width] imul bx, font_width add bx, 5+5-1 mov ecx, [con.def_wnd_y-2] mov cx, word [con.wnd_height] imul cx, font_height lea ecx, [ecx+eax+5-1] mov edx, 0x33000000 mov edi, [con.title] ; place for scrollbar mov eax, [con.wnd_height] cmp eax, [con.scr_height] jae @f add ebx, con.vscroll_width @@: xor eax, eax int 0x40 call con.draw_image push 12 pop eax push 2 pop ebx int 0x40 ret con.draw_image: xor edx, edx mov ecx, [con.wnd_width] imul ecx, font_width mov [con.data_width], cx shl ecx, 16 mov cx, word [con.wnd_height] imul cx, font_height mov [con.data_height], cx mov ebx, [con.image] push 65 pop eax xor ebp, ebp mov edi, con.colors push 8 pop esi int 0x40 mov al, 7 mov edx, [con.wnd_height] cmp edx, [con.scr_height] jae .skip_vscroll push ecx mov edx, ecx xor dx, dx mov ebx, con.vscroll_btn3 cmp [con.bUpPressed], 0 jnz @f mov ebx, con.vscroll_btn1 @@: mov ecx, con.vscroll_width*65536 + con.vscroll_btn_height int 0x40 pop edx sub dx, con.vscroll_btn_height mov ebx, con.vscroll_btn4 cmp [con.bDownPressed], 0 jnz @f mov ebx, con.vscroll_btn2 @@: int 0x40 push edx ; Вычисляем высоту бегунка mov ax, dx sub eax, con.vscroll_btn_height mov ecx, eax mul [con.wnd_height] div [con.scr_height] cmp eax, 5 jae @f mov al, 5 @@: ; eax = высота бегунка. Вычисляем положение бегунка mov [con.vscrollbar_size], eax xchg eax, ecx sub eax, ecx mul [con.wnd_ypos] mov ebx, [con.scr_height] sub ebx, [con.wnd_height] div ebx pop edx push edx ; ecx = высота бегунка, eax = положение add eax, con.vscroll_btn_height mov [con.vscrollbar_pos], eax mov ebx, con.vscroll_bgr2 cmp [con.bScrollingUp], 0 jnz @f mov ebx, con.vscroll_bgr1 @@: mov ecx, con.vscroll_width*65536 + con.vscroll_bgr_height push eax push 7 pop eax mov dx, con.vscroll_btn_height call .vpattern mov dx, word [con.vscrollbar_pos] add dx, word [con.vscrollbar_size] mov cx, con.vscroll_bgr_height mov ebx, con.vscroll_bgr2 cmp [con.bScrollingDown], 0 jnz @f mov ebx, con.vscroll_bgr1 @@: call .vpattern mov ecx, [con.vscrollbar_pos] mov dx, cx add ecx, [con.vscrollbar_size] sub ecx, con.vscroll_bar_height3 push ecx mov ebx, con.vscroll_bar1 mov ecx, con.vscroll_width*65536 + con.vscroll_bar_height1 int 0x40 add dx, cx mov cx, con.vscroll_bar_height2 mov ebx, con.vscroll_bar2 call .vpattern mov ebx, con.vscroll_bar3 mov cx, con.vscroll_bar_height3 int 0x40 .skip_vscroll: ret .vpattern: push edx add dx, cx cmp dx, [esp+8] pop edx jbe @f mov cx, [esp+4] sub cx, dx jz .ret @@: int 0x40 add dx, cx cmp dx, [esp+4] jb .vpattern .ret: ret 4 align 4 con.colors dd 0x000000, 0x000080, 0x008000, 0x008080 dd 0x800000, 0x800080, 0x808000, 0xC0C0C0 dd 0x808080, 0x0000FF, 0x00FF00, 0x00FFFF dd 0xFF0000, 0xFF00FF, 0xFFFF00, 0xFFFFFF scan_has_ascii: dd 11011111111111111111111111111110b dd 00000010001111111111101111111111b dd 00000000000000000000000000000000b dd 0 con.extended_alt: db 00h,01h,78h,79h,7Ah,7Bh,7Ch,7Dh,7Eh,7Fh,80h,81h,82h,83h,0Eh,0A5h db 10h,11h,12h,13h,14h,15h,16h,17h,18h,19h,1Ah,1Bh,1Ch,00h,1Eh,1Fh db 20h,21h,22h,23h,24h,25h,26h,27h,28h,29h,00h,2Bh,2Ch,2Dh,2Eh,2Fh db 30h,31h,32h,33h,34h,35h,00h,37h,00h,39h,00h,68h,69h,6Ah,6Bh,6Ch db 6Dh,6Eh,6Fh,70h,71h,00h,00h,97h,98h,99h,4Ah,9Bh,9Ch,9Dh,4Eh,9Fh db 0A0h,0A1h,0A2h,0A3h,00h,00h,00h,8Bh,8Ch,00h,00h,00h,00h,00h,00h,00h times 20h db 0 con.extended_ctrl: times 0Fh db %-1 db 0x94 times 2Bh db %-1 db 5Eh,5Fh,60h,61h,62h,63h,64h,65h,66h,67h,00h,00h db 77h,8Dh,84h,8Eh,73h,8Fh,74h,90h,75h,91h,76h,92h,93h,00h,00h,00h,89h,8Ah times 0x80-0x59 db 0 con.extended_shift: times 3Bh db %-1 db 54h,55h,56h,57h,58h,59h,5Ah,5Bh,5Ch,5Dh,00h,00h db 47h,48h,49h,4Ah,4Bh,4Ch,4Dh,4Eh,4Fh,50h,51h,52h,53h,00h,00h,00h,87h,88h times 0x80-0x59 db 0 ; В текущей реализации значения по умолчанию таковы. ; В будущем они, возможно, будут считываться как параметры из ini-файла console.ini. con.def_wnd_width dd 80 con.def_wnd_height dd 25 con.def_scr_width dd 80 con.def_scr_height dd 300 con.def_wnd_x dd 200 con.def_wnd_y dd 50 con.vscroll_pt dd -1 align 16 EXPORTS: dd szStart, START dd szVersion, 0x00020006 dd szcon_init, con_init dd szcon_write_asciiz, con_write_asciiz dd szcon_printf, con_printf dd szcon_exit, con_exit dd szcon_get_flags, con_get_flags dd szcon_set_flags, con_set_flags dd szcon_kbhit, con_kbhit dd szcon_getch, con_getch dd szcon_getch2, con_getch2 dd szcon_gets, con_gets dd szcon_gets2, con_gets2 dd szcon_get_font_height, con_get_font_height dd szcon_get_cursor_height,con_get_cursor_height dd szcon_set_cursor_height,con_set_cursor_height dd szcon_cls, con_cls dd szcon_get_cursor_pos, con_get_cursor_pos dd szcon_set_cursor_pos, con_set_cursor_pos dd 0 con_flags dd 7 con.cursor_height dd (15*font_height+50)/100 con.input_start dd con.input_buffer con.input_end dd con.input_buffer con_esc_attr_n dd 0 con_esc_attrs dd 0,0,0,0 con_esc db 0 con_sci db 0 con.entered_char dw -1 con.bGetchRequested db 0 con.bWasE0 db 0 szStart db 'START',0 szcon_init db 'con_init',0 szcon_write_asciiz db 'con_write_asciiz',0 szcon_printf db 'con_printf',0 szcon_exit db 'con_exit',0 szVersion db 'version',0 szcon_get_flags db 'con_get_flags',0 szcon_set_flags db 'con_set_flags',0 szcon_kbhit db 'con_kbhit',0 szcon_getch db 'con_getch',0 szcon_getch2 db 'con_getch2',0 szcon_gets db 'con_gets',0 szcon_gets2 db 'con_gets2',0 szcon_get_font_height db 'con_get_font_height',0 szcon_get_cursor_height db 'con_get_cursor_height',0 szcon_set_cursor_height db 'con_set_cursor_height',0 szcon_cls db 'con_cls',0 szcon_get_cursor_pos db 'con_get_cursor_pos',0 szcon_set_cursor_pos db 'con_set_cursor_pos',0 con.thread_err db 'Cannot create console thread!',13,10,0 con.nomem_err db 'Not enough memory!',13,10,0 con.aFinished db ' [Finished]',0 con.aNull db '(null)',0 con.beep db 0x90, 0x3C, 0x00 con.ipc_buf dd 0,8,0,0 db 0 section '.data' data readable writable align 16 con.finished_title rb 256 con.cur_x rd 1 con.cur_y rd 1 con.wnd_xpos rd 1 con.wnd_ypos rd 1 con.wnd_width rd 1 con.wnd_height rd 1 con.scr_width rd 1 con.scr_height rd 1 con.title rd 1 con.data rd 1 con.image rd 1 con.console_tid rd 1 con.data_width rw 1 con.data_height rw 1 con.vscrollbar_size rd 1 con.vscrollbar_pos rd 1 con.up_first_time rd 1 con.down_first_time rd 1 con.scroll_up_first_time rd 1 con.scroll_down_first_time rd 1 con.bUpPressed_saved rb 1 con.bDownPressed_saved rb 1 con.bScrollingUp_saved rb 1 con.bScrollingDown_saved rb 1 con.input_buffer rw 128 con.input_buffer_end = $ con.kbd_layout rb 128 ; 1 = exit, 2 = set title, 3 = redraw, 4 = getch con.thread_op rb 1 con.bUpPressed rb 1 con.bDownPressed rb 1 con.bScrollingUp rb 1 con.bScrollingDown rb 1 con.stack rb 1024 con.stack_top = $