//----------------------------------------------------------------- // arrowpad.s // // This example shows how you can directly program the height // and location of the cursor while in standard text display- // mode. Here the keyboard is "polled" for new input, rather // than being "interrupt-driven" which is done customarily. // // to assemble: $ as arrowpad.s -o arrowpad.o // and to link: $ ld arrowpad.o -T ldscript -o arrowpad.b // and install: $ dd if=arrowpad.b of=/dev/sda4 seek=1 // // NOTE: This code begins executing with CS:IP = 1000:0002. // // programmer: ALLAN CRUSE // written on: 09 OCT 2008 //----------------------------------------------------------------- # EQUATES for some i/o port-addresses .equ CRTC_PORT, 0x3D4 # port for CRT controller .equ INTA_MASK, 0x21 # port for Master PIC Mask .equ KB_STATUS, 0x64 # port for KEYBD Status .equ KB_DATA, 0x60 # port for KEYBD Data # EQUATES for some keyboard scancodes .equ KEYPAD_MIN, 0x47 # scancode for -key .equ KEYPAD_MAX, 0x51 # scancode for -key .equ KEY_ESCAPE, 0x01 # scancode for .section .text #------------------------------------------------------------------ .word 0xABCD # programming signature #------------------------------------------------------------------ main: .code16 # execution in 'real-mode' mov %sp, %cs:ldrtos+0 # save loader's SP mov %ss, %cs:ldrtos+2 # also loader's SS mov %cs, %ax # address this segment mov %ax, %ds # with DS register mov %ax, %ss # also SS register lea tos, %sp # establish new stack call initialize_video_ram call expand_cursor_height call exec_arrow_keys_demo lss %cs:ldrtos, %sp # recover loader's stack lret # back to program loader #------------------------------------------------------------------ ldrtos: .word 0, 0 # holds loader's stacktop #------------------------------------------------------------------ # storage-area for display-page and cursor-location coordinates page: .byte 0 # video page-number row: .byte 0 # cursor row-number col: .byte 0 # cursor col-number #------------------------------------------------------------------ #------------------------------------------------------------------ initialize_video_ram: # set the CRT display_mode for standard 80-column text mov $0x0003, %ax # set_mode: 80x25 text int $0x10 # request BIOS service # setup initial display-memory address in registers ES:DI mov $0xB800, %ax # address video memory mov %ax, %es # with ES register xor %di, %di # initial vram offset # setup initial picture-element value in register AX mov $0x30, %al # ascii page-number '0' mov $0x07, %ah # normal color-attribute cld # do forward processing # fill each vram page with that page's page-number mov $8, %dx # number of vram pages nxpg: mov $2048, %cx # characters-per-page rep stosw # fill w/char and color inc %al # set next page-number dec %dx # decrement loop-count jnz nxpg # initialize next page ret #------------------------------------------------------------------ exec_arrow_keys_demo: # mask the keyboard-controller interrupts in $INTA_MASK, %al # master-8259A mask or $0x02, %al # set mask for IRQ1 out %al, $0x21 # write the new mask kbpoll: # wait till keyboard controller reports output-buffer full in $KB_STATUS, %al # get controller status test $0x01, %al # output buffer full? jz kbpoll # no, keep on polling # input the new keyboard-scancode into register AL in $KB_DATA, %al # get keyboard scancode # check for our loop-exit condition cmp $KEY_ESCAPE, %al # -key? je finis # yes, exit this loop # otherwise process this newest scancode call move_cursor # perform cursor action jmp kbpoll # then get next scancode finis: # unmask keyboard-controller interrupts in $INTA_MASK, %al # master-8259A mask and $0xFD, %al # clear mask for IRQ1 out %al, $0x21 # write the new mask ret #------------------------------------------------------------------ #------------------------------------------------------------------ expand_cursor_height: mov $CRTC_PORT, %dx # CRTC port-address mov $0x020A, %ax # cursor_start: line 2 out %ax, %dx # write to CRTC register mov $0x0C0B, %ax # cursor_end: line 12 out %ax, %dx # write to CRTC register ret #------------------------------------------------------------------ jump_table: .word do_home_key, do_arrow_up, do_page_up, ignore .word do_arrow_left, do_page_0, do_arrow_right, ignore .word do_end_key, do_arrow_down, do_page_down move_cursor: # check whether keyboard-scancode in AL is 'out-of-range' cmp $KEYPAD_MIN, %al # scancode below minimum? jb movxx # yes, take no action cmp $KEYPAD_MAX, %al # scancode above maximum? ja movxx # yes, take no action # compute the scancode's jump-table array-index sub $KEYPAD_MIN, %al # subtract table minimum movzx %al, %eax # then extend to 32-bits # perform an 'indirect call' through our jump-table call *jump_table(,%eax,2) # modify cursor coordinates # reprogram cursor's location based on modified coordinates call compute_params # recalculate parameters call reprogram_crtc # program CRTC registers movxx: ret # return to the caller #----------------------------------------------------------------- start_address: .word 0 # for CRTC start_address cursor_offset: .word 0 # for offset within page #----------------------------------------------------------------- compute_params: # compute: start_address = page_number * cells_per_page mov page, %bl # get vram page-number and $0x0007, %bx # extended to 16-bits shl $11, %bx # multiply by 2048 mov %bx, start_address # save as start_address # compute: cursor_offset = row * cells_per_row + column mov $80, %al # cells_per_row into AL mulb row # times the row-number add col, %al # add the column-number adc $0, %ah # as a 16-bit summand mov %ax, cursor_offset # save as cursor_offset ret #----------------------------------------------------------------- #----------------------------------------------------------------- reprogram_crtc: # reprogram the CRTC start_address mov $CRTC_PORT, %dx # setup CRTC port-address mov start_address, %bx # fetch the start-address mov $0x0D, %al # index for start_addr_lo mov %bl, %ah # value for start_addr_lo out %ax, %dx # write new start_addr_lo mov $0x0C, %al # index for start_addr_hi mov %bh, %ah # value for start_addr_hi out %ax, %dx # write new start_addr_hi # reprogram the CRTC cursor-position mov $CRTC_PORT, %dx # setup CRTC port-address mov start_address, %bx # fetch the start-address add cursor_offset, %bx # plus cursor page-offset mov $0x0F, %al # index for cursor_lo mov %bl, %ah # value for cursor_lo out %ax, %dx # write new cursor_lo mov $0x0E, %al # index for cursor_hi mov %bh, %ah # value for cursor_hi out %ax, %dx # write new cursor_hi ret #------------------------------------------------------------------ # EQUATES for our arrowpad actions .equ MINROW, 0 .equ MAXROW, 24 .equ MINCOL, 0 .equ MAXCOL, 79 .equ MIN_PG, 0 .equ MAX_PG, 7 .equ CHINCR, 1 .equ LNINCR, 1 .equ PGINCR, 25 .equ PGMASK, 7 #------------------------------------------------------------------ #============= Below are the jump-table routines ================ #------------------------------------------------------------------ do_home_key: movb $MINROW, row movb $MINCOL, col ret #------------------------------------------------------------------ do_end_key: movb $MAXROW, row movb $MAXCOL, col ret #------------------------------------------------------------------ #------------------------------------------------------------------ do_arrow_up: subb $LNINCR, row jge upok movb $MAXROW, row subb $PGINCR, page andb $PGMASK, page upok: ret #------------------------------------------------------------------- do_arrow_down: addb $LNINCR, row cmpb $MAXROW, row jle dnok movb $MINROW, row addb $PGINCR, page andb $PGMASK, page dnok: ret #------------------------------------------------------------------- do_arrow_left: subb $CHINCR, col jge lfok movb $MAXCOL, col subb $LNINCR, row jge lfok movb $MAXROW, row subb $PGINCR, page andb $PGMASK, page lfok: ret #------------------------------------------------------------------- do_arrow_right: addb $CHINCR, col cmpb $MAXCOL, col jle rtok movb $MINCOL, col addb $LNINCR, row cmpb $MAXROW, row jle rtok addb $PGINCR, page andb $PGMASK, page rtok: ret #------------------------------------------------------------------- do_page_up: subb $PGINCR, page andb $PGMASK, page ret #------------------------------------------------------------------- do_page_down: addb $PGINCR, page andb $PGMASK, page ret #------------------------------------------------------------------- do_page_0: movb $MIN_PG, page ret #------------------------------------------------------------------- ignore: ret # no cursor movement #------------------------------------------------------------------- .align 16 # assure stack alignment .space 512 # reserved for stack use tos: # label for top-of-stack #------------------------------------------------------------------- .end # nothing more to assemble