//------------------------------------------------------------------- // vesademo.s // // This example demonstrates use of Video BIOS Extensions for // reprogramming the Super-VGA graphics controller's hardware // to allow drawing of images in a 'truecolor' graphics mode. // // to assemble: $ as vesademo.s -o vesademo.o // and to link: $ ld vesademo.o -T ldscript -o vesademo.b // and install: $ dd if=vesademo.b of=/dev/sda4 seek=1 // // NOTE: This program begins executing with CS:IP = 1000:0002. // // programmer: ALLAN CRUSE // date begun: 22 NOV 2008 // completion: 25 NOV 2008 //------------------------------------------------------------------- .equ VESA_MODE, 0x0115 # 800x600, 32bpp .equ TRUE_CYAN, 0x0000FFFF # RGB color-components .equ TRUE_YELLOW, 0x00FFFF00 # RGB color-components .section .text #------------------------------------------------------------------- .word 0xABCD # our application signature #------------------------------------------------------------------- main: .code16 # start in x86 'real-mode' mov %sp, %cs:ipltos+0 # preserve the loader's SP mov %ss, %cs:ipltos+2 # preserve the loader's SS mov %cs, %ax # address program's data mov %ax, %ss # with SS register lea tos, %sp # and setup new stacktop call get_mode_information call turn_on_the_A20_line call enable_graphics_mode call enter_protected_mode call execute_program_demo call leave_protected_mode call resume_standard_text lss %cs:ipltos, %sp # recover saved SS and SP lret # exit back to the loader #------------------------------------------------------------------- ipltos: .word 0, 0 # for loader's SS and SP #------------------------------------------------------------------- theGDT: .word 0x0000, 0x0000, 0x0000, 0x0000 # null descriptor .equ sel_es, (.-theGDT)+0 # vram-segment's selector .word 0x0007, 0x8000, 0x920B, 0x0080 # vram descriptor .equ sel_cs, (.-theGDT)+0 # code-segment's selector .word 0xFFFF, 0x0000, 0x9A01, 0x0000 # code descriptor .equ sel_ds, (.-theGDT)+0 # data-segment's selector .word 0xFFFF, 0x0000, 0x9201, 0x0000 # data descriptor .equ sel_fs, (.-theGDT)+0 # flat-segment's selector .word 0xFFFF, 0x0000, 0x9200, 0x008F # flat descriptor .equ sel_gs, (.-theGDT)+0 # graf-segment's selector .word 0xFFFF, 0x0000, 0x9200, 0x008F # graf descriptor .equ limGDT, (.-theGDT)-1 # our GDT-segment's limit #------------------------------------------------------------------- theIDT: .space 256 * 8 # for 256 IDT-descriptors .equ limIDT, (.-theIDT)-1 # our IDT-segment's limit #------------------------------------------------------------------- regGDT: .word limGDT, theGDT, 0x0001 # register-image for GDTR regIDT: .word limIDT, theIDT, 0x0001 # register-image for IDTR regIVT: .word 0x03FF, 0x0000, 0x0000 # register-image for IDTR #------------------------------------------------------------------- enter_protected_mode: cli # no device interrupts mov %cr0, %eax # get machine status bts $0, %eax # set PE-bit to 1 mov %eax, %cr0 # enable protection lgdt %cs:regGDT # setup GDTR register lidt %cs:regIDT # setup IDTR register ljmp $sel_cs, $pm # reload register CS pm: mov $sel_ds, %ax mov %ax, %ss # reload register SS xor %ax, %ax # use "null" selector mov %ax, %ds # to purge invalid DS mov %ax, %es # to purge invalid ES mov %ax, %fs # to purge invalid FS mov %ax, %gs # to purge invalid GS ret #------------------------------------------------------------------- leave_protected_mode: mov $sel_ds, %ax # address 64K r/w segment mov %ax, %ds # using DS register mov %ax, %es # and ES register mov %ax, %fs # and FS register mov %ax, %gs # and GS register mov %cr0, %eax # get machine status btr $0, %eax # reset PE-bit to 0 mov %eax, %cr0 # disable protection ljmp $0x1000, $rm # reload register CS rm: mov %cs, %ax mov %ax, %ss # reload register SS lidt %cs:regIVT # restore vector table sti # and allow interrupts ret #------------------------------------------------------------------- execute_program_demo: mov %esp, %ss:tossav+0 # preserve 32-bit offset mov %ss, %ss:tossav+4 # plus 16-bit selector movl $TRUE_YELLOW, %ss:c call draw_rectangle call wait_for_keypress movl $TRUE_CYAN, %ss:c call draw_annulus call wait_for_keypress lss %cs:tossav, %esp # reload our saved SS:ESP ret # return to main function #------------------------------------------------------------------- tossav: .long 0, 0 # stores a 48-bit pointer #------------------------------------------------------------------- hex: .ascii "0123456789ABCDEF" # array of hex digits #------------------------------------------------------------------- eax2hex: # converts value in EAX to hexadecimal string at DS:EDI pushal mov $8, %ecx # setup digit counter nxnyb: rol $4, %eax # next nybble into AL mov %al, %bl # copy nybble into BL and $0xF, %ebx # isolate nybble's bits mov hex(%ebx), %dl # lookup ascii-numeral mov %dl, (%edi) # put numeral into buf inc %edi # advance buffer index loop nxnyb # back for next nybble popal ret #------------------------------------------------------------------- pitch: .int 0 # for bytes-per-scanline h_res: .int 0 # for pixels-per-row v_res: .int 0 # for pixels-per-column p_res: .int 0 # for bytes-per-pixel pelsz: .int 0 # for bits_per_pixel fbptr: .int 0 # for frame-buffer address #------------------------------------------------------------------- get_mode_information: # call VBE function 1 to get graphics-mode information mov %ss, %ax # address program arena mov %ax, %ds # using DS register mov %ax, %es # and ES register lea modeInfoBlock, %di # point ES:DI to buffer mov $VESA_MODE, %ecx # specify desired mode mov $0x4F01, %eax # specify VBE function int $0x10 # invoke BIOS service # extract needed values from the modeInfoBlock xor %eax, %eax # clear the accumulator mov modeInfoBlock+25, %al # get bits-per-pixel mov %eax, pelsz # store as an integer mov modeInfoBlock+16, %ax # get bytes-per-scanline mov %eax, pitch # store as an integer mov modeInfoBlock+18, %ax # get horiz-resolution mov %eax, h_res # store as an integer mov modeInfoBlock+20, %ax # get vertl-resolution mov %eax, v_res # store as an integer mov modeInfoBlock+40, %eax # get frame-buffer address mov %eax, fbptr # store as an integer # compute the pixel-size in bytes mov pelsz, %eax # pixel-size in bits shr $3, %eax # divide by bits/byte mov %eax, p_res # store as an integer # setup global descriptor for the frame-buffer memory mov $sel_gs, %ebx # descriptor's selector shr $3, %ebx # get its table-index lea theGDT(, %ebx, 8), %si # point DS:SI to descriptor mov fbptr, %eax # address of frame-buffer mov %ax, 2(%si) # bits 15..0 into word 2 rol $16, %eax # bits 31..16 into EAX mov %al, 4(%si) # bits 23..16 into byte 4 mov %ah, 7(%si) # bits 31..24 into byte 7 # format frame-buffer address mov fbptr, %eax lea fbptr$, %edi call eax2hex # format horiziontal-resolution mov h_res, %eax lea h_res$, %edi call eax2hex # format vertical-resolution mov v_res, %eax lea v_res$, %edi call eax2hex # format scanline's size (i.e., 'pitch') mov pitch, %eax lea pitch$, %edi call eax2hex # format picture-element size (bits-per-pixel) mov pelsz, %eax lea pelsz$, %edi call eax2hex # format the Global Descriptor Table's quadword entry lea descr$, %di # point to dest'n field mov 4(%si), %eax # get quadword upper-part call eax2hex # convert to hexadecimal add $8, %di # advance field address mov 0(%si), %eax # get quadword lower-part call eax2hex # convert to hexadecimal # display the graphics-mode's parameters mov $0x0F, %ah # get_video_page int $0x10 # invoke BIOS service mov $0x03, %ah # get_cursor_position int $0x10 # invoke BIOS service lea msg1, %bp # point ES:BP to string mov len1, %cx # string-length into CX mov att1, %bl # text-attributes in BL mov $0x1301, %ax # write_string function int $0x10 # invoke BIOS service # wait for the user to press a key mov $0x00, %ah # get_keyboard_data int $0x16 # invoke BIOS service ret #------------------------------------------------------------------- msg1: .ascii "\r\n " pitch$: .ascii "xxxxxxxx = pitch \r\n " h_res$: .ascii "xxxxxxxx = h_res \r\n " v_res$: .ascii "xxxxxxxx = v_res \r\n " pelsz$: .ascii "xxxxxxxx = pelsz \r\n " fbptr$: .ascii "xxxxxxxx = fpptr \r\n " .ascii "\r\n " descr$: .ascii "xxxxxxxxxxxxxxxx = descriptor \r\n " .ascii "\r\n" len1: .short . - msg1 att1: .byte 0x0B #------------------------------------------------------------------- c: .int 0 # current color-value x: .int 0 # current x-coordinate y: .int 0 # current y-coordinate xmax: .int 0 # maximum x-coordinate ymax: .int 0 # maximum y-coordinate xmin: .int 0 # minimum x-coordinate ymin: .int 0 # minimum y-coordinate rmax: .int 0 # annulus outer-radius rmin: .int 0 # annulus inner-radius xcent: .int 0 # center's x-coordinate ycent: .int 0 # center's y-cootdinate #------------------------------------------------------------------- # This macro computes the location for (x,y) and draws pixel-color .macro put_pixel mov pitch, %eax # get scanline width mull y # times scanline number mov %eax, %edi # copy to EDI register mov p_res, %eax # get pixel's width mull x # times column-number add %eax, %edi # add to EDI register mov c, %eax # load current pixel-color mov %eax, %gs:(%edi) # write to screen-location .endm #------------------------------------------------------------------- draw_rectangle: # # This rectangle-drawing algorithm moves the current (x,y) location # around the rectangle's edges, as in this C-implementation: # # do { vram[ y, x ] = c; ++x; } while ( x < xmax ); # do { vram[ y, x ] = c; ++y; } while ( y < ymax ); # do { vram[ y, x ] = c; --x; } while ( x > xmin ); # do { vram[ y, x ] = c; --y; } while ( y > ymin ); # mov $sel_ds, %ax # address program arena mov %ax, %ds # using DS register mov $sel_gs, %ax # address frame-buffer mov %ax, %gs # with GS register mov h_res, %eax # horizontal-resolution dec %eax # reduced by one mov %eax, xmax # is maximum x-coordinate mov v_res, %eax # vertical-resolution dec %eax # reduced by one mov %eax, ymax # is maximum y-coordinate nxtop: put_pixel # draw pixel at (x,y) addl $1, x # then increment x mov xmax, %eax # fetch maximum x cmpl %eax, x # x below maximum? jl nxtop # yes, draw another nxrhs: put_pixel # draw pixel at (x,y) addl $1, y # then increment y mov ymax, %eax # fetch maximum y cmpl %eax, y # y below maximum? jl nxrhs # yes, draw another nxbot: put_pixel # draw pixel at (x,y) subl $1, x # then decrement x mov xmin, %eax # fetch minimum x cmpl %eax, x # x above minimum? jg nxbot # yes, draw another nxlhs: put_pixel # draw pixel at (x,y) subl $1, y # then decrement y mov ymin, %eax # fetch minimum y cmpl %eax, y # y above minimum? jg nxlhs # yes, draw another ret #------------------------------------------------------------------- draw_annulus: # # This algorithm moves the current (x,y) location over the entire # screen-area, row-by-row, left-to-right, checking whether or not # the distance from the center is within the annulus's two radii; # if so, the pixel-color is drawn; otherwise, drawing is skipped. # mov $sel_ds, %ax # address program arena mov %ax, %ds # using DS register mov $sel_gs, %ax # address frame-buffer mov %ax, %gs # with GS register mov h_res, %eax # horizontal-resolution shr $1, %eax # is halved and saved mov %eax, xcent # as center x-coordinate mov v_res, %eax # vertical-resolution shr $1, %eax # is halved and saved mov %eax, ycent # as center y-coordinate mov h_res, %eax # horizontal-resolution shr $2, %eax # is halved twice for mov %eax, rmax # outer annulus-radius shr $1, %eax # and halved again as mov %eax, rmin # inner annulus-radius mov ymin, %eax # topmost row-number mov %eax, y # is starting y-value nx_y: mov xmin, %eax # leftmost column-number mov %eax, x # is starting x-value nx_x: mov x, %eax # current x-coordinate sub xcent, %eax # minus center's x imul %eax # difference is squared mov %eax, %ebx # and saved in EBX mov y, %eax # current y-coordinate sub ycent, %eax # minus center's y imul %eax # difference is squared add %eax, %ebx # and added to EBX mov rmax, %eax # outer annulus-radius imul %eax # radius is squared cmp %ebx, %eax # (x,y) lies outside? jl doadv # yes, no pixel drawn mov rmin, %eax # inner annulus-radius imul %eax # radius is squared cmp %ebx, %eax # (x,y) lies inside? jg doadv # yes, no pixel drawn put_pixel # else draw pixel-color doadv: addl $1, x # increment x-coordinate mov xmax, %eax # fetch maximum x cmp %eax, x # x below maximum? jl nx_x # yes, draw another addl $1, y # increment y-coordinate mov ymax, %eax # fetch maximum y cmp %eax, y # y below maximum? jl nx_y # yes, draw another ret #------------------------------------------------------------------- wait_for_keypress: spin: in $0x64, %al # keyboard's status test $0x01, %al # new scancode arrived? jz spin # no, wait for arrival in $0x60, %al # else input scancode test $0x80, %al # key was released? jz spin # no, wait for release ret #------------------------------------------------------------------- enable_graphics_mode: mov $0x4F02, %eax # VESA set_mode function mov $VESA_MODE, %ebx # VESA mode's identifier int $0x10 # invoke BIOS service ret #------------------------------------------------------------------- turn_on_the_A20_line: in $0x92, %al # System Control settings or $0x02, %al # set 'Fast-A20' (bit #1) out %al, $0x92 # output the new settings ret #------------------------------------------------------------------- resume_standard_text: mov $0x0003, %ax # set 80x25 text-mode int $0x10 # invoke BIOS service ret #------------------------------------------------------------------- .align 16 # assure stack alignment .space 512 # reserved for stack use tos: # label for top-of-stack #------------------------------------------------------------------- modeInfoBlock: # label for storage area #------------------------------------------------------------------- .end # nothing more to assemble