java 画图 怎么清除已经画出来的图形_计算机自制操作系统(九):漂亮的桌面是怎么来的...

本文介绍了如何在计算机操作系统中实现图形界面,包括使用BIOS中断画图、直接写显存地址画图以及支持高分辨率的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

d75d0bed0051a73b6c9f45478c539327.png

如果计算机一直都是DOS系统那样的黑底白字界面,就早已失去了它的魅力。我们制作的操作系统目前看起来很不友好,之前在“(五):引导和内核程序组织方式”一章中,我做了一个如下的“图形化”界面:

93871a50a901a918559e0ea319a2816f.png

但严格意义上讲,这不能算得上是一个图形化的界面,图形明显看上去比较模糊和粗糙。因为它是利用的BIOS的INT 10H中的"开窗填充"功能,在“文本模式”下把80*25的相关字符位设置了特殊颜色而已:

554c9e7cfeaa1f1ce60adcb797d59a3d.png

af77efbe80490068ea0525889b30e7fa.png

我们制作的操作系统一定是要支持像Windows那样的窗口和桌面。那么,我们怎么才能实现漂亮的图形界面呢?这就是必须要切换到“图形模式”:

c635f327343635631effbcee8399e166.png

一、利用BIOS中断画图

一般情况下,可以运用BIOS的INT 10H的“写图形象素”来实现对每个象素点的颜色填充,最终就可以绘制出高质量的图形化界面图,下面我们就用试着用以下程序来创建一个桌面。

%define   com
%ifdef    com
orgaddr   equ   100h
%else
orgaddr   equ   7c00h
%endif

jmp   start

welcome db 'Welcome Jiang OS!','$'

start:

mov ax, cs
add ax, orgaddr/10h
mov ds, ax
mov es, ax

mov si, welcome   ;开机默认文本模式
call   printstr
call   newline

call   windows    ;切换到图形模式

mov ah,4ch
int 21h
jmp $


printstr:                  ;显示指定的字符串, 以'$'为结束标记
      mov al,[si]
      cmp al,'$'
      je disover
      mov ah,0eh
      int 10h
      inc si
      jmp printstr
disover:
      ret

newline:                     ;显示回车换行
      mov ah,0eh
      mov al,0dh
      int 10h
      mov al,0ah
      int 10h
      ret


windows:
    call setmode
    call alllines    ;画背景蓝色
    call win1
    call win2
    call win3
    call win4
    ret


setmode:
    mov  bx,0x4105    ;设置图形模式:1024×768 256色
    mov  ax,0x4f02
    int 10h
    ret


oneline:                 ;该程序是在第dx行画水平线
        ;mov cx,0        ;x坐标
        ;mov dx,0        ;y坐标
        mov al,0001b     ;颜色
        mov ah,0ch       ;写入点像
goon:   inc cx
        cmp cx,1023
        int 10h
        jne goon
        ret

alllines: mov dx,0      ;该程序画满整屏水平线
 goon2:   mov cx,0
          call  oneline
          inc dx
          cmp dx,767
          jne goon2
          ret

 win1:    mov dx,200      ;画矩形
  linew1:  mov cx,300
  goonw1: mov al,0100b     ;颜色
        mov ah,0ch       ;写入点像
        inc cx
        cmp cx,500
        int 10h
        jne goonw1
        inc dx
        cmp dx,400
        jne linew1
        ret


 win2:    mov dx,200      ;画矩形
  linew2:  mov cx,520
  goonw2: mov al,0010b     ;颜色
        mov ah,0ch       ;写入点像
        inc cx
        cmp cx,720
        int 10h
        jne goonw2
        inc dx
        cmp dx,400
        jne linew2
        ret


 win3:    mov dx,420      ;画矩形
  linew3:  mov cx,300
  goonw3: mov al,0110b     ;颜色
        mov ah,0ch       ;写入点像
        inc cx
        cmp cx,500
        int 10h
        jne goonw3
        inc dx
        cmp dx,620
        jne linew3
        ret


 win4:    mov dx,420      ;画矩形
  linew4:  mov cx,520
  goonw4: mov al,0111b     ;颜色
        mov ah,0ch       ;写入点像
        inc cx
        cmp cx,720
        int 10h
        jne goonw4
        inc dx
        cmp dx,620
        jne linew4
        ret

运行之后,发现“Windows"窗口如期所现:

59b4bdc1534fd01150a7dbad0045c1ea.png

这是伟大的一刻!因为我从知道计算机开始,就一直对冷冰冰的机器为什么能显示出那么缤纷色彩的世界感到疑惑。通过自己的这一实践,总算明白了其中的原理,不知道当初对我说是用“Visaul Basic”做出来的那位同学明白没有。当然,计算机能够迈向丰富的多媒体世界,其实最要感谢的应该是显示器的显示技术。因为,CPU只是给出了1010的数据,但人家显示器却给能给出这么神奇的色彩!

从我们的试验可以看出,计算机能用不同的颜色来控制每一个象素,那么它就能绘制出任何的影像图形了,所以漂亮的桌面就是这么来的。接下来的显示照片、播放视频、网页冲浪、游戏翻转等无一不是漂亮图形的功劳......

二、直接写显存地址画图

BIOS的 INT 10h中画图由于调用的时候包装的东西太多,因此执行速度很慢,在程序运行的时候居然能看到“刷屏”动作,所以基本没人会用它。为了达到正常使用的目标,我们就不能偷懒,只有绕过BIOS中断自己通过直接写显存的方式来画图。

我们知道,在文本模式下,直接写显存的方式很简单:字符+字符颜色。在图形模式下,字符不存在,因此象素颜色就成了唯一需要控制的显示数据,似乎更加的简单。比如我们选择的是256色,那我们用一个字节指定一个颜色的值直接送往显卡象素对应的内存,岂不就可以了?

开始我是这样想的,可是实际却不是这样的。因为这里有一个调色板的概念:当我们用一个数字要指定要显示256颜色中具体某种色彩的时候,我们还必须要先自己定义好这个颜色。因为显示器不支持你给它一个数值,它就立即显示出一个颜色。这样的设计是可以理解的,如果用户不能自定义颜色,那么所有的显示器都需要支持标准化的配置,比如01代表红色,02代表蓝色等等,这样显示器的制作就很僵化,而且都只能局限于标准的CPU定义的256种颜色。

有了调色板技术,实现过程就比较自由化了。当我们选择256颜色的时候,系统默认的是RGB三基色使用的是6BIT,因此每种分色支持64种设置,这样组合起来总共的颜色数量就是:64^3=262144。这个结论说明:大部分人理解的选择256颜色就最多只能支持256颜色是错误的,实际上系统上最多可以设置的颜色数量是262144,但是由于我们在256模式下,内存中是用一个字节来显示象素,所以我们最多只能同时从262144中选择256色显示而已。

调色板的概念就是用来设置262144种颜色,我们必须要通过设置调色板来先自定义颜色,方法是向显示器发送设置调色板的指令。指定包括调色板功能端口(3c8h)和调色板颜色设置端口(3c9h)。设置调色板的时候,需要定义索引编号,以便我们后面需要显示某种色彩的时候,在显存地址的象素位置处用索引编号来调用。256颜色模式的调色板索引编号为0-255,规定0号为屏幕的背景色。所以默认情况下,我们没有设置调色板的时候,它是黑色的,可以看到电脑刚进入图形模式的时候都是黑色的屏幕。后面再从1号开始,就可以自定义调色板的各种颜色了。

需要注意的是256以及以下的颜色模式需要用调色板技术,而后面的更高分辨率在256以上颜色模式的时候就不需要调色板了,那叫真色彩技术(可以理解为现在我们只用了262144种颜色中的256种,因此是“假颜色”)。如在bmp图片格式里看到的24位位图真色彩图片,其颜色种类是:256^3(RGB每个分量都用1Byte表示)。

256模式下,一些典型的颜色RGB分量值如下:

1.当RGB分量值相等的时候,是黑白色,灰度不同而已:

89c3cfeb7e9566629c62a496bfffebd8.png

2.当RGB分量值有两种颜色=0的时候,是纯色,要增加纯色亮度,可以提高另外两个的值。

9d77fa9c7429653a018cb6d97f9fc43e.png

3.其它混合色

ccb5541523f235308f49a64d9b6019e9.png

基于以上知识,我们就可以用以下程序通过直接写显存的方式来进行图形显示:

colorfuncport equ 3c8h   ;设置调色板功能端口
colorsetport equ 3c9h    ;设置调色板颜色端口
displayadd equ 0xa000    ;图像模式显存起始地址


call  setmode
call  backgroud
call  colorset
call  win1
call  win2
call  win3
call  win4
mov   ax,0ch
int   21h
jmp   $


setmode:
mov ah,0
mov al,13h         ;320*200
int 10h
ret


setmode2:
mov AX,4F02H
mov bx,4105H       ;1024*768
int 10h
ret

backgroud:         ;背景色设置
mov dx,  colorfuncport
mov al,  0           ;建调色板索引0号
out dx,al

mov dx,  colorsetport   ;设置蓝色背景
mov al,0           ;R分量
out dx,al
mov al,0           ;G分量
out dx,al
mov al,35          ;B分量
out dx,al
ret

colorset:             ;显示色设置
mov dx,  colorfuncport
mov al,  1              ;建调色板索引1号
out dx,al

mov dx,  colorsetport     ;设置白色调色板
mov al,63           ;R分量
out dx,al
mov al,63           ;G分量
out dx,al
mov al,63          ;B分量
out dx,al

mov dx,  colorfuncport
mov al,  2                 ;建调色板索引2号
out dx,al

mov dx,  colorsetport     ;设置红色调色板
mov al,63           ;R分量
out dx,al
mov al,0           ;G分量
out dx,al
mov al,0          ;B分量
out dx,al

mov dx,  colorfuncport
mov al, 3           ;建调色板索引3号
out dx,al

mov dx,  colorsetport     ;设置黄色
mov al,30           ;R分量
out dx,al
mov al,30           ;G分量
out dx,al
mov al,0          ;B分量
out dx,al

mov dx,  colorfuncport
mov al, 4              ;建调色板索引4号
out dx,al

mov dx,  colorsetport     ;设置黑色
mov al,0           ;R分量
out dx,al
mov al,0           ;G分量
out dx,al
mov al,0          ;B分量
out dx,al

ret


drawimg:           ;满屏画同一颜色
mov bl,4
mov ax,displayadd
mov es,ax
mov cx,0xffff
mov di,0
nextpoint:
mov  [es:di],bl   ;调色板颜色索引送往显存地址
inc di
loop  nextpoint
ret

win1:
mov bl,1              ;填充颜色
mov dx,50             ;起始行
mov ax,320*50/10h
add ax,displayadd
mov es,ax
win1line:
mov cx,50         ;矩形长度
mov di,100         ;起始列
win1point:
mov  [es:di],bl   ;调色板颜色索引送往显存地址
inc di
loop win1point
inc dx
mov ax,es
add ax,0x14      ;显卡内存每一行增加一次es段地址   320*x+y 320=140h
mov es,ax
cmp dx,100       ;矩形高度
jne win1line
ret

win2:
mov bl,2
mov dx,110         ;起始行
mov ax,320*110/10h
add ax,displayadd
mov es,ax
win2line:
mov cx,50         ;矩形长度
mov di,100         ;起始列
win2point:
mov  [es:di],bl   ;调色板颜色索引送往显存地址
inc di
loop win2point
inc dx
mov ax,es
add ax,0x14      ;显卡内存每一行增加一次es段地址   320*x+y 320=140h
mov es,ax
cmp dx,160       ;矩形高度
jne win2line
ret

win3:
mov bl,3
mov dx,50         ;起始行
mov ax,320*50/10h
add ax,displayadd
mov es,ax
win3line:
mov cx,50          ;矩形长度
mov di,160         ;起始列
win3point:
mov  [es:di],bl   ;调色板颜色索引送往显存地址
inc di
loop win3point
inc dx
mov ax,es
add ax,0x14      ;显卡内存每一行增加一次es段地址   320*x+y 320=140h
mov es,ax
cmp dx,100       ;矩形高度
jne win3line
ret

win4:
mov bl,4
mov dx,110         ;起始行
mov ax,320*110/10h
add ax,displayadd
mov es,ax
win4line:
mov cx,50          ;矩形长度
mov di,160         ;起始列
win4point:
mov  [es:di],bl   ;调色板颜色索引送往显存地址
inc di
loop win4point
inc dx
mov ax,es
add ax,0x14      ;显卡内存每一行增加一次es段地址   320*x+y 320=140h
mov es,ax
cmp dx,160       ;矩形高度
jne win4line
ret

运行的“Windows”窗口(分辨率320*200)如下,而且不会有速度慢的情况。

fc6db79f7252ae08004993f85c70bca1.png

看一下真机启动的运行效果:

8999e081120be3c1c7c5ba239729fff3.png

三、如何支持高分辨率

对于上面直接写显存的方法,最关键地方是抓住“图形模式”的显存起始地址是:a0000h。对于256色模式,显然是一个象素一个字节,那么我们只需要在0xa0000基础上逐步后移填充数据就能依次点亮满屏色彩。

我以为万事大吉了,可是当我把分辨率从320*200换到640*480的时候,就出问题了。因为,我在0xa0000基础上依次填满0xffff(64K)个象素点之后,再往后面填充颜色,就不起作用了:

b7c8cdc5d5d9f17a9437b335f62d4acb.png

这就说明,内存空间从a0000h开始之后,最多只能有0xffff的空间给显存使用,就只能到0xaffff。0xffff=64K,象素数量约等于320*200的分辨率,因此在分辨率>320*200的情况下,就没有办法完整控制显卡了。那我们要使用更高分辨率该怎么办呢?

我通过查资料得出结论:由于显示器的内存量一般远远大于64K,那我们要在指定的64K空间里实现高分辨率的控制,就必须要使用“分页”映射的技术,显存将分页映射到段址A000开始的64K区域,通过切换页就能访问全部的显存了。

这种映射技术无疑是复杂的,而且效率低下。我们可以自己思考:这样做无非是想节约内存的空间嘛,不然我们直接把所有象素位置全部分配到起始地址0xa0000不就行了。你还别说,这种思路是可行的,那就是“利用线性地址模式”来访问显存。它允许我们自定义显存在内存中的映射地址(不从0xa0000开始),这样我们就可以想显示多少象素就显示多少了!

线性地址模式写显存:方法就是利用显卡自定义线性地址功能,通过命令端口让其反馈在内存中的映射地址,这样就可以把要显示的象素颜色直接写入映射的地址中了。

下面的程序就是用显存线性地址的方法来进行绘图。由于涉及图形显示模式,因此最好不要用DOS环境来进行调试,我们直接通过制作MBR的方式来做:

;%define   com             ;注释用于MBR,不注释用于COM调试
%ifdef    com
orgaddr   equ   100h       ;用于COM调试的设置
%else
orgaddr   equ   7c00h      ;用于MBR启动的设置
%endif

VESA equ 0

jmp start

start:
mov ax, cs
add ax, orgaddr/10h
mov ds, ax
mov es, ax


call  setmode
call  getdisplayaddr
call  drawimg2


%ifdef    com           ;如果是.COM调试,需要调用DOS返回中断
mov ah,4ch
int 21h
%endif                  ;.COM程序将在此顺利返回Dos系统

jmp $                   ;如果是MBR启动,将在此停止运行

setmode:
mov AX,4F02H
mov bx,4101H        ;640*480  256色   第1个值4表示为要使用线性地址模式
int 10h


;取得该模式下显存线性地址,调用0x4F01 功能号,获得信息
getdisplayaddr:
mov ax,0x9000    ;指定一个放置显存线性地址的地方
mov es,ax
mov di,  VESA

mov ax , 0x4f01
mov cx , 0x101
int 0x10
;返回的结构体中偏移量为40 的地方,即es:di+40 处,用4字节存放了一个线性地址,
;这就是这个显卡在此模式下显存的线性地址

;dw  0x1234 --- 0x34 0x12  注意DWORD的内存放置顺序,低地址为低位,高地址为高位
mov bx,[ es:VESA+40 ]    ;0000 取出线性显存的低16位地址
mov dx,[ es:VESA+42 ]    ;C000 取出线性显存的高16位地址
ret


;向取得的显存地址里写数据直接绘制图形
drawimg2:
;mov  ax,0xa000         ;这里用0xa000是测试图像显示,说明显示子程序没有问题
;mov  es,ax             ;但真正这里需要的是用dx和bx组合的32位线性显存地址来绘图
mov cx,0xffff
mov bl,3
mov si,0
nextpoint2:
mov  [es:si],bl   ;调色板颜色索引送往显存地址
inc si
loop  nextpoint2
ret

times 510-($-$$) db 0
      db 0x55,0xaa

该程序的流程是:先设置好“640*480 256色”的显示模式,再向显卡通过4f01查询此模式下的显存线性地址,该中断返回的地址数据将会存储在es:di+40 处。但是,这是一个32位的地址,所以程序最后我将高16位的地址存入了DX,将低16位的地址存入了BX。通过bochs调试器(调试方法见本专栏文章十七)的来跟踪本MBR程序中DX和BX之值,可以看出此模式下,显卡的线性地址值是:0xE0000000。

66ea8ab9cdd6854c4e83842b51d1aad5.png

最后一步就应该是往显卡线性地址0xE0000000里写数据进行显示验证了。可是,这里的问题就出现了:显存线性地址是一个32位的值,我们通过DS(ES)最多可以能寻址访问的内存地址是:0XFFFF:0XFFFF,也即0XFFFFF。显然没有办法进行下去了,因为0XFFFFF=1MB,也即最多支持1MB的内存空间,现在0xE0000000已经远远超出这个空间了!所以,这里我们的实验就只能暂时终止,没有办法进行显示器“640*480 256色”图形颜色模式的显示效果演示。

这就是16位计算机的局限性,所以我们的操作系统下一步的目标就必须要进入32位环境。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值