基于ega位面,把我头都给弄大。哪位大虾能否分析该MASM宏汇编。

时间:2022-10-14 01:14:37
Stack   segment para stack 'STACK'
        db      512 dup(0)
Stack   ends

SCREEN_WIDTH_IN_BYTES    equ     80
DISPLAY_MEMORY_SEGMENT   equ     0a000h
SC_INDEX         equ     3c4h    ;Sequence Controller Index register
MAP_MASK         equ     2       ;index of Map Mask register
GC_INDEX         equ     03ceh   ;Graphics Controller Index reg
GRAPHICS_MODE    equ     5       ;index of Graphics Mode reg
BIT_MASK equ     8       ;index of Bit Mask reg

Data    segment para common 'DATA'
;
; Current location of "A" as it is animated across the screen.
;
CurrentX        dw      ?
CurrentY        dw      ?
RemainingLength dw      ?
;
; Chunky bit-map image of a yellow "A" on a bright blue background
;
AImage          label   byte
                dw      13, 13          ;width, height in pixels
                db      000h, 000h, 000h, 000h, 000h, 000h, 000h
                db      009h, 099h, 099h, 099h, 099h, 099h, 000h
                db      009h, 099h, 099h, 099h, 099h, 099h, 000h
                db      009h, 099h, 099h, 0e9h, 099h, 099h, 000h
                db      009h, 099h, 09eh, 0eeh, 099h, 099h, 000h
                db      009h, 099h, 0eeh, 09eh, 0e9h, 099h, 000h
                db      009h, 09eh, 0e9h, 099h, 0eeh, 099h, 000h
                db      009h, 09eh, 0eeh, 0eeh, 0eeh, 099h, 000h
                db      009h, 09eh, 0e9h, 099h, 0eeh, 099h, 000h
                db      009h, 09eh, 0e9h, 099h, 0eeh, 099h, 000h
                db      009h, 099h, 099h, 099h, 099h, 099h, 000h
                db      009h, 099h, 099h, 099h, 099h, 099h, 000h
                db      000h, 000h, 000h, 000h, 000h, 000h, 000h
Data    ends

Code    segment para public 'CODE'
        assume  cs:Code, ds:Data
Start   proc    near
        mov     ax,Data
        mov     ds,ax
        mov     ax,12h
        int     10h              ;select video mode 10h (640x350)
;
; Prepare for animation.
;
        mov     [CurrentX],0
        mov     [CurrentY],200
        mov     [RemainingLength],600    ;move 600 times
;
; Animate, repeating RemainingLength times. It's unnecessary to erase
; the old image, since the one pixel of blank fringe around the image
; erases the part of the old image not overlapped by the new image.
;
AnimationLoop:
        mov     bx,[CurrentX]
        mov     cx,[CurrentY]
        mov     si,offset AImage
        call    DrawFromChunkyBitmap     ;draw the "A" image
        inc     [CurrentX]               ;move one pixel to the right

        mov     cx,0                     ;delay so we don't move the
DelayLoop:                               ; image too fast; adjust as
                                         ; needed
        loop    DelayLoop

        dec     [RemainingLength]
        jnz     AnimationLoop
;
; Wait for a key before returning to text mode and ending.
;
        mov     ah,01h
        int     21h
        mov     ax,03h
        int     10h
        mov     ah,4ch
        int     21h
Start   endp
;
; Draw an image stored in a chunky-bit map into planar VGA/EGA memory
; at the specified location.
;
; Input:
;       BX = X screen location at which to draw the upper left corner
;               of the image
;       CX = Y screen location at which to draw the upper left corner
;               of the image
;       DS:SI = pointer to chunky image to draw, as follows:
;               word at 0: width of image, in pixels
;               word at 2: height of image, in pixels
;               byte at 4: msb/lsb = first & second chunky pixels,
;                       repeating for the remainder of the scan line
;                       of the image, then for all scan lines. Images
;                       with odd widths have an unused null nibble
;                       padding each scan line out to a byte width
;
; AX, BX, CX, DX, SI, DI, ES destroyed.
;
DrawFromChunkyBitmap    proc    near
        cld
;
; Select write mode 2.
; 选择写方式2
        mov     dx,GC_INDEX
        mov     al,GRAPHICS_MODE
        out     dx,al
        inc     dx
        mov     al,02h
        out     dx,al
;
; Enable writes to all 4 planes.
;
        mov     dx,SC_INDEX       ;选择时序寄存器
        mov     al,MAP_MASK       
        out     dx,al              ;选择映像屏蔽寄存器(选择那些位面有效)
        inc     dx
        mov     al,0fh              
        out     dx,al              ;位面全部有效
;
; Point ES:DI to the display memory byte in which the first pixel
; of the image goes, with AH set up as the bit mask to access that
; pixel within the addressed byte.
;
        mov     ax,SCREEN_WIDTH_IN_BYTES        
        mul     cx               ;offset of start of top scan line
        mov     di,ax
        mov     cl,bl
        and     cl,111b
        mov     ah,80h           ;set AH to the bit mask for the
        shr     ah,cl            ; initial pixel
        shr     bx,1
        shr     bx,1
        shr     bx,1             ;X in bytes
        add     di,bx            ;offset of upper left byte of image
        mov     bx,DISPLAY_MEMORY_SEGMENT
        mov     es,bx            ;ES:DI points to the byte at which the
                                 ; upper left of the image goes
;
; Get the width and height of the image.
;
        mov     cx,[si]          ;get the width
        inc     si
        inc     si
        mov     bx,[si]       ;get the height
        inc     si
        inc     si
        mov     dx,GC_INDEX          ; 选择图像控制寄存器
        mov     al,BIT_MASK          ; 选择位屏蔽寄存器
        out     dx,al            ;leave the GC Index register pointing
        inc     dx               ; to the Bit Mask register
RowLoop:                                ;行循环

        push    ax               ;preserve the left column's bit mask
        push    cx               ;preserve the width
        push    di               ;preserve the destination offset

ColumnLoop:                             ;列循环
        mov     al,ah
        out     dx,al            ;set the bit mask to draw this pixel   为要写的像素设置位掩码
        mov     al,es:[di]       ;load the latches   装载锁存器
        mov     al,[si]          ;get the next two chunky pixels
       shr     al,1             ;从此行开始到最后一行都不明白
       shr     al,1
       shr     al,1
       shr     al,1              ;move the first pixel into the lsb
       stosb                    ;draw the first pixel
        ror     ah,1             ;move mask to next pixel position
        jc      CheckMorePixels  ;is next pixel in the adjacent byte? 接下来的像素在相邻字节吗
        dec     di               ;no

CheckMorePixels:
        dec     cx               ;see if there are any more pixels
        jz      AdvanceToNextScanLine ; across in image
        mov     al,ah
        out     dx,al            ;set the bit mask to draw this pixel
        mov     al,es:[di]       ;load the latches
        lodsb                    ;get the same two chunky pixels again
        stosb                    ;draw the second of the two pixels
        ror     ah,1             ;move mask to next pixel position
        jc      CheckMorePixels2 ;is next pixel in the adjacent byte?
        dec     di               ;no
CheckMorePixels2:
        loop    ColumnLoop       ;see if there are any more pixels
        jmp     short CheckMoreScanLines
AdvanceToNextScanLine:
        inc     si              ;advance to the start of the next
                              
CheckMoreScanLines:
        pop     di              ;get back the destination offset
        pop     cx              ;get back the width
        pop     ax              ;get back the left column's bit mask
        add     di,SCREEN_WIDTH_IN_BYTES
        dec     bx              ;see if there are any more scan lines
        jnz     RowLoop         ; in the image
        ret
DrawFromChunkyBitmap    endp
Code    ends
        end     Start
各位帮帮忙啊

46 个解决方案

#1


运行一下此程序只发现屏幕右"A".

#2


郁闷,我早就知道老

#3


这是VGA,不是EGA。640*480,16色显示模式。16色显示模式显存分为8个位平面映射,也就是一个字节内存映射8个像素点,通过向3CE端口输出08H再向3CF端口输出位平面掩码来切换。向显存输出前必须先读一下以打开映射通道,否则输出无效。因为16色只需要4位数,一个字节可以储存两个像素的值,向显存输出时只有低4位是有效的,连续四个shr al,1就是把高4位移到低4位上,8086没有shr al,4这样的指令。

#4


gz

#5


1:"连续四个shr al,1就是把高4位移到低4位上"

按你这么一说,那我低四位的像素不是被一走了吗?

2:“这是VGA,不是EGA。640*480,16色显示模式。16色显示模式显存分为8个位平面映射,也就是一个字节内存映射8个像素点”

何以见得,代码中的“mov     al,0fh”足以说明是是4个位面

#6


1、因为Image数据中1字节表示两个像素颜色,高4位与低4位各表示一个像素,右移四位是准备输出高4位,低四位在后面的代码中输出。
2、下面这两行指令是把显示模式设置为640*480,16色:
mov     ax,12h 
int     10h              ;select video mode 10h (640x350) 
这个注释信息是错的,如果要640x350,应该ax=10H。

#7


呵呵,谢谢你"CNZDGS".
我现在有点糊涂,您能不能把多个CPU字节(至少2字节)按写方式2写入各位面(郁闷,具体是几个我不知道!)的过程,以及其分布情况详细的阐述一遍,谢谢

#8


如果要连续填充同一颜色(如画横线),可以一次写多个位面,对于不同颜色只能一次写一个位面。
分布情况是这样:
A000:0这个字节对应屏幕上最上面一行最左边的8个像素,位面0~7分别对应第0到第7像素;A000:1这个字节对应第8到第15这8个像素,同样是每个位面对应一个像素;……一行排完紧接着排下一行。当你要在屏幕上画一个点(x,y)时,先计算y*640+x,其中640是每行的像素个数,然后除以8,商就是显存映射地址的偏移量,余数是位面代号。

#9


这就怪了,书上说CPU字节的每个为对应一个位面。如CPU字节0位对应位面0,1位对应位面1,以此类推。另外则是位掩码对各个位面分布情况又是怎么样?????????


现在满脑袋问好,只要能弄懂我多加分就是了

#10


你说的书上这段话是说读像素,读和写是不同的。不清楚的地方做程序试一下就清楚了。

#11


vga12H是640*480,16色,我以前学过一个vga12的c语言汉字处理程序。后来我用汇编vga12h显示了一个4位的图片,用的是out in,现在忘了,现在这个12h不行了,才16色,现在一般都是256色以上,8位的,16/24/32 位的,所以13h是256色,但分辨太低,我只道的只能用svga显卡,vesa标准,是高分辨,高位深。但现在没有svga卡了,我知道一个dos32a扩展器,qv多媒体播放器,可以显示24位图片。

#12


"如果要连续填充同一颜色(如画横线),可以一次写多个位面,对于不同颜色只能一次写一个位面。"怎么实现

#13


晕,一个颜色4位,那跟一个像素4有什么关系,VGA读取一个像素是以4位为单元吗?

#14


当要画一个点时,用写模式2,根据像素位置确定位面;
当要画一条横线时,用写模式0,同时选择8个位面,这样一次是写入8个点。
读像素是分4次读,每次选择一个位面读出一位数据,再组合到一起。

#15


哈哈,头脑终于又清晰多了,老大您能加我QQ吗,我想给你细谈这个问题,这样问题解决起来快些!!!

QQ:305513011

#16


我没装各种聊天软件,也没有时间聊,只能偶尔抽空看看贴,有问题就在这问吧。不过这方面的程序我已经很多年都没做过了,很多东西记不太清了。

#17




        shr     al,1             ;从此行开始到最后一行都不明白 
        shr     al,1 
       shr     al,1 
       shr     al,1              ;move the first pixel into the lsb 
       stosb                    ;draw the first pixel 
        ror     ah,1             ;move mask to next pixel position 
        jc      CheckMorePixels  ;is next pixel in the adjacent byte? 接下来的像素在相邻字节吗 
        dec     di               ;no 

CheckMorePixels: 
        dec     cx               ;see if there are any more pixels 
        jz      AdvanceToNextScanLine ; across in image 
        mov     al,ah 
        out     dx,al            ;set the bit mask to draw this pixel 
        mov     al,es:[di]       ;load the latches 
        lodsb                    ;get the same two chunky pixels again 
        stosb                    ;draw the second of the two pixels 
        ror     ah,1             ;move mask to next pixel position 
        jc      CheckMorePixels2 ;is next pixel in the adjacent byte? 
        dec     di               ;no 
CheckMorePixels2: 
        loop    ColumnLoop       ;see if there are any more pixels 
        jmp     short CheckMoreScanLines 
AdvanceToNextScanLine: 
        inc     si              ;advance to the start of the next 
                               
CheckMoreScanLines: 
        pop     di              ;get back the destination offset 
        pop     cx              ;get back the width 
        pop     ax              ;get back the left column's bit mask 
        add     di,SCREEN_WIDTH_IN_BYTES 
        dec     bx              ;see if there are any more scan lines 
        jnz     RowLoop         ; in the image 
        ret 
DrawFromChunkyBitmap    endp 
Code    ends 
        end     Start 


1:代码的看得想发火了,页面怎么这么难,也不给个好的解释!!!!!!!!!!!!!
cnzdgs您行行好,再多做件对我们这些初学者有益的事,好吗,谢谢了

2:上面的代码对我来说没有一点进展,我不明白他把AImage中的每一字节的后四位舍弃的目的何在,并且你所说八个位面我表示怀疑,因为VGA的源码书上也没有提到任何关于8个位面的显存(也就是640×480*16)。而你所说8位面是指的"640×480*256"

#18


我给给解释一下这段代码:
       shr     al,1
        shr     al,1
       shr     al,1
       shr     al,1
al中是两个像素,高4位是前1个,低4位后1个。al右移4为,把al中的前1个像素移动到低4位,后1个像素暂时抛弃
       stosb
写入前1个像素,此时写入的低4位有效,高4位自动抛弃
       ror     ah,1
ah中是位面掩码,右移1位,准备切换到下1位面
       jc      CheckMorePixels
ah的8位数据中只有一个1,其余都是0,从最高位开始向低位移,当这个1移出(循环到最高位同时CF置位时)就表示下一像素应该写入下一字节
        dec     di
ah中的1未移出则还是当前字节,所以di减1,(因为stosb会使di加1)
CheckMorePixels:
       dec     cx
减计数器
       jz      AdvanceToNextScanLine
计数器为0则这行像素画完了
       mov     al,ah
       out     dx,al
切换位面
       mov     al,es:[di]
执行一下读操作,否则无法写入
       lodsb
再取出[si]中的值,这次是输出低4位,然后si自动加1,指向下1字节
       stosb
写入后1像素,同样是低4位有效,高4位抛弃
       ror     ah,1
准备切换到下一位面
       jc      CheckMorePixels2
判断是否ah中的1移出
       dec     di
ah中的1未移出则下一像素还是这个字节
CheckMorePixels2:
       loop    ColumnLoop
减计数器并循环

#19


对于16色显示模式,在写模式2中,A000段中每个字节对应8个像素,分别在8个位面上,不要把位面与二进制的位联系到一起。
256色比16色简单得多,每个字节就是一个像素,直接读写,只是需要切换显存映射窗口。

#20



cnzdgs谢谢你这几天坚持回帖,我发觉我看不懂VGA代码的关键在出现我理解不了。颜色值,像素值,位面,它们之间关系!
颜色值就是像素值吗,颜色值或像素值跟位面的关系?

#21


其实这些名词没有统一的定义,通常说的颜色值就是像素值,如果严格讲起来是这样:颜色值是由红、绿、蓝(R、G、B)三部分组成,每部分用1字节表示,取值范围是0~255;对于256色以下的图象数据,用索引值表示,索引值就是该像素的颜色在调色板中序号(基于0),显存中储存的也是索引值。
位面是从Plane翻译过来的,因为使用某些显示模式时,每个内存地址对应多个显存地址,所以把显存形象化地分成多个平面,每个内存地址在各个平面上都有一个投影,就是映射。至于“位”字,应该是来自于bit mark,在选择Plane是需要输出一个位掩码,每一位对应一个Plane,为1的表示选中,为0的表示屏蔽。

#22


哦,原来是这样啊,再次感谢。那么通过位掩码码选在一个位例如:bit mark:0001则选择位面0,我通过向该显存地址写一个字节,只有有这个位面才接受四个像素位(颜色),那么VGA通过"读"哪些位面来输入屏幕呢,而且读取是1位还是4位(这跟"读"操作有关吗)

#23


读的时候是4个位面,每个位面包含像素的一位,所以要分4次来读,每读一位切换一次位面,每次读出的数据中只有对应位是有效的,要自己把有效位提取出来。

#24


你真及时啊!!!学习了,从您上面的分析来看,bit mark包含在一字节中,是吧?

#25


#26


看了一天的代码,还是没什么收获。一会儿又是4位一个像素,一会儿又一字节一个像素。现在到好彻底的混为一谈了!!

#27


请问您能否在线聊天(WEB)。

#28


我看你是离线状态。
你只要知道:在16色显示模式下,读一个像素是从4个位面各读一位数据;写一个像素是向1个位面写一字节数据(低4位有效)就可以了。

#29


cnzdgs这几天都多亏你的帮忙,我才对VGA有了一定的了解。

#30


不客气。这方面还是要自己做程序去试才能掌握清楚,看别人的代码是比较费劲的。

#31


不知道是不是
CheckMoreScanLines:
        pop     di              ;get back the destination offset
        pop     cx              ;get back the width
        pop     ax              ;get back the left column's bit mask
        add     di,SCREEN_WIDTH_IN_BYTES  ;开始下一扫描线

#32


#33


.model small
 vgaseg equ 0a000h
sequen_ctl equ 3c4h
graphi_ctl equ 3ceh
Onscreen_X equ 20
Onscreen_y equ 100
linelen equ 640/8
only_one_pixel equ 003h
.code
start:

          mov ax,vgaseg  ;ES段指向显存地址0a000h
          mov es,ax
          mov  cx,0
          mov  dx,184Fh
          mov  bh,07
          mov  ax,600h
          int  10h
          
          ;没有堆栈
          
          mov ax, 12h
          int 10h
          
          mov ax,Onscreen_y
          push ax
          mov ax,Onscreen_X
          push ax
          call enable_write_mode_one
enable_write_mode_one proc
          mov bp,sp
          mov ax,[bp]   ;取x
          mov cl,3
          shr bx,cl    ;X0/8
          mov ax,[bp+2]
          mov cx,linelen
          mul cx
          add ax,bx           ;该坐标所在内存字节
          mov di,ax
          
          ;设置映象屏蔽寄存器
          mov dx,sequen_ctl
          mov al,2
          out dx,al
          inc dx
          mov al,0fh   ;四个位面全部充许写
          out dx,al
          ;选择写方式2
          mov dx,graphi_ctl
          mov al,5
          out dx,al
          inc dx
          mov al,2
          out dx,al
          ;计算屏蔽码,并设置位屏蔽寄存器
   point: dec dx
          mov al,8
          out dx,al         
          mov cx,[bp]  ;取出X在一个位面上位置
          and cl,7
          mov al,80h
          shr al,cl
          inc dx
          out dx,al   
          ror al,1
          jc done
          ;写数据
          mov ax,only_one_pixel    ;取颜色值
          mov ah,es:[di]    ;读一次使屏蔽寄存器有效
          mov es:[di],al ;写入显示存储器
          mov ax,[bp]
          inc ax
          mov [bp],ax
          jmp point
          
          done:nop
          jmp done
 
enable_write_mode_one endp
 end start

从POINT开始我的目的是显示一个点,但结果是一条线

#34


你这是一个循环,把jmp point去掉就是一个点了。另外
ror al,1
jc done
这两行应该去掉。

#35


啊,为什么,我按你所说的理解为:一个颜色,进行各位屏蔽后才写入位面,我的那个循环也是这个目的。我那理解错了!

#36


而且像素显示的位置也不对

#37


像素显示的位置这个问题解决了,堆栈处理不当

#38


设置位屏蔽寄存器只需要一次输出,输出的字节中为1的那一位对应位面被选中,其它位面都屏蔽。

#39


还有就是为什么要把X坐标除8,X不是已经是个字节(像素),还是它的这个像素概念是相对于位面???

假设颜色数值是003h(因为有8位,所以有8个像素),即二进制0000011。设x=1,那么x坐标求余为1,相反屏蔽了位6,那么剩下5位,我怎么办了,我通过循环也就是这个目的

#40


你现在理解的很混乱。
在写模式2下,A000段的每个字节对应的是8个像素,这8个像素各对应一个位面。在写像素之前要通过设置位屏蔽寄存器来控制选择其中1个位面,屏蔽其它7个位面。屏蔽寄存器中的8位二进制数中每1位对应一个位面,为1的位面有效,为0位面屏蔽。
当要写一个像素时,首先根据像素坐标(X,Y),先计算Y*640+X,这是总的像素序号,然后把Y*640+X除以8,商就是该像素在A000段的偏移量(将其保存到DI),余数是位面代号(将其保存到CL),把80H右移CL位,然后输出到位屏蔽寄存器,这样就选中了该像素对应的位面同时屏蔽了其它无关位面,然后把A000:DI的数据先读出一下,再把要写入的像素值写入,这样就完成了。

#41


我有两本VGA方面的电子版,说的都是很笼统,所以有时候分布清那是对的那是错的。。
1:你的意思是说一个页面有640*480个像素吗。
2:"余数是位面代号(将其保存到CL),把80H右移CL位",这只是单个位面吧,我的意思就是想使用XOR CL,1移动掩码位啊,就像我上面代码那样。

#42


1、这种显示模式整个屏幕就是640*480个像素;
2、写一个点就是要选一个位面,如果是画线才需要循环切换位面。

#43


总之,我还是谢谢你这几天回复,的确我被VGA各种操作搞混了,还是专注的看另一本书吧!:-)

#44




|--|                            ----------------------|
|1 |   0位面的1个字节                                   |
|  |     从最高位开始                                        |
|--|    每个时钟周期移动一个BIT                           |      
                                                        |   
|--|                                                    |
| 1|     1位面的1个字节                                         | 
|  |     从最高位开始                                           |
|--|    每个时钟周期移动一个BIT                             |
                                                          |   四位颜色值
                                                           |
|--|                                                      |
| 2|       2位面的1个字节                                     |
|  |     从最高位开始                                         |
|--|      每个时钟周期移动一个BIT                          |
                                                       |
|--|                                               |
|3 |        3位面的1个字节                            |
|  |       从最高位开始                               | 
|--|     每个时钟周期移动一个BIT     -------------|



迷糊了,怎么又成一个字节了啊

#45


是不是0~3位面相同位对应一个4位值

#46


跟时钟周期没有关系。

写的时候有8个位面,每个位面中的1字节(只有低4位有效)对应1个像素值。
读的时候有4个位面,4个位面中的同一位对应1个像素值的4位数据。

建议你先做一个写单一像素的函数,可以在屏幕上任意位置画一个点,然后再练习用画点的方式贴图;写单一像素熟悉了之后,再做一个画横线函数,可以在屏幕上任意位置画任意长度的横线,然后再练习画矩形;然后再做一个读单一像素的函数;再做读一个读横线函数,再练习读矩形。到这基本就都掌握了,可以再熟悉其它操作模式。

#1


运行一下此程序只发现屏幕右"A".

#2


郁闷,我早就知道老

#3


这是VGA,不是EGA。640*480,16色显示模式。16色显示模式显存分为8个位平面映射,也就是一个字节内存映射8个像素点,通过向3CE端口输出08H再向3CF端口输出位平面掩码来切换。向显存输出前必须先读一下以打开映射通道,否则输出无效。因为16色只需要4位数,一个字节可以储存两个像素的值,向显存输出时只有低4位是有效的,连续四个shr al,1就是把高4位移到低4位上,8086没有shr al,4这样的指令。

#4


gz

#5


1:"连续四个shr al,1就是把高4位移到低4位上"

按你这么一说,那我低四位的像素不是被一走了吗?

2:“这是VGA,不是EGA。640*480,16色显示模式。16色显示模式显存分为8个位平面映射,也就是一个字节内存映射8个像素点”

何以见得,代码中的“mov     al,0fh”足以说明是是4个位面

#6


1、因为Image数据中1字节表示两个像素颜色,高4位与低4位各表示一个像素,右移四位是准备输出高4位,低四位在后面的代码中输出。
2、下面这两行指令是把显示模式设置为640*480,16色:
mov     ax,12h 
int     10h              ;select video mode 10h (640x350) 
这个注释信息是错的,如果要640x350,应该ax=10H。

#7


呵呵,谢谢你"CNZDGS".
我现在有点糊涂,您能不能把多个CPU字节(至少2字节)按写方式2写入各位面(郁闷,具体是几个我不知道!)的过程,以及其分布情况详细的阐述一遍,谢谢

#8


如果要连续填充同一颜色(如画横线),可以一次写多个位面,对于不同颜色只能一次写一个位面。
分布情况是这样:
A000:0这个字节对应屏幕上最上面一行最左边的8个像素,位面0~7分别对应第0到第7像素;A000:1这个字节对应第8到第15这8个像素,同样是每个位面对应一个像素;……一行排完紧接着排下一行。当你要在屏幕上画一个点(x,y)时,先计算y*640+x,其中640是每行的像素个数,然后除以8,商就是显存映射地址的偏移量,余数是位面代号。

#9


这就怪了,书上说CPU字节的每个为对应一个位面。如CPU字节0位对应位面0,1位对应位面1,以此类推。另外则是位掩码对各个位面分布情况又是怎么样?????????


现在满脑袋问好,只要能弄懂我多加分就是了

#10


你说的书上这段话是说读像素,读和写是不同的。不清楚的地方做程序试一下就清楚了。

#11


vga12H是640*480,16色,我以前学过一个vga12的c语言汉字处理程序。后来我用汇编vga12h显示了一个4位的图片,用的是out in,现在忘了,现在这个12h不行了,才16色,现在一般都是256色以上,8位的,16/24/32 位的,所以13h是256色,但分辨太低,我只道的只能用svga显卡,vesa标准,是高分辨,高位深。但现在没有svga卡了,我知道一个dos32a扩展器,qv多媒体播放器,可以显示24位图片。

#12


"如果要连续填充同一颜色(如画横线),可以一次写多个位面,对于不同颜色只能一次写一个位面。"怎么实现

#13


晕,一个颜色4位,那跟一个像素4有什么关系,VGA读取一个像素是以4位为单元吗?

#14


当要画一个点时,用写模式2,根据像素位置确定位面;
当要画一条横线时,用写模式0,同时选择8个位面,这样一次是写入8个点。
读像素是分4次读,每次选择一个位面读出一位数据,再组合到一起。

#15


哈哈,头脑终于又清晰多了,老大您能加我QQ吗,我想给你细谈这个问题,这样问题解决起来快些!!!

QQ:305513011

#16


我没装各种聊天软件,也没有时间聊,只能偶尔抽空看看贴,有问题就在这问吧。不过这方面的程序我已经很多年都没做过了,很多东西记不太清了。

#17




        shr     al,1             ;从此行开始到最后一行都不明白 
        shr     al,1 
       shr     al,1 
       shr     al,1              ;move the first pixel into the lsb 
       stosb                    ;draw the first pixel 
        ror     ah,1             ;move mask to next pixel position 
        jc      CheckMorePixels  ;is next pixel in the adjacent byte? 接下来的像素在相邻字节吗 
        dec     di               ;no 

CheckMorePixels: 
        dec     cx               ;see if there are any more pixels 
        jz      AdvanceToNextScanLine ; across in image 
        mov     al,ah 
        out     dx,al            ;set the bit mask to draw this pixel 
        mov     al,es:[di]       ;load the latches 
        lodsb                    ;get the same two chunky pixels again 
        stosb                    ;draw the second of the two pixels 
        ror     ah,1             ;move mask to next pixel position 
        jc      CheckMorePixels2 ;is next pixel in the adjacent byte? 
        dec     di               ;no 
CheckMorePixels2: 
        loop    ColumnLoop       ;see if there are any more pixels 
        jmp     short CheckMoreScanLines 
AdvanceToNextScanLine: 
        inc     si              ;advance to the start of the next 
                               
CheckMoreScanLines: 
        pop     di              ;get back the destination offset 
        pop     cx              ;get back the width 
        pop     ax              ;get back the left column's bit mask 
        add     di,SCREEN_WIDTH_IN_BYTES 
        dec     bx              ;see if there are any more scan lines 
        jnz     RowLoop         ; in the image 
        ret 
DrawFromChunkyBitmap    endp 
Code    ends 
        end     Start 


1:代码的看得想发火了,页面怎么这么难,也不给个好的解释!!!!!!!!!!!!!
cnzdgs您行行好,再多做件对我们这些初学者有益的事,好吗,谢谢了

2:上面的代码对我来说没有一点进展,我不明白他把AImage中的每一字节的后四位舍弃的目的何在,并且你所说八个位面我表示怀疑,因为VGA的源码书上也没有提到任何关于8个位面的显存(也就是640×480*16)。而你所说8位面是指的"640×480*256"

#18


我给给解释一下这段代码:
       shr     al,1
        shr     al,1
       shr     al,1
       shr     al,1
al中是两个像素,高4位是前1个,低4位后1个。al右移4为,把al中的前1个像素移动到低4位,后1个像素暂时抛弃
       stosb
写入前1个像素,此时写入的低4位有效,高4位自动抛弃
       ror     ah,1
ah中是位面掩码,右移1位,准备切换到下1位面
       jc      CheckMorePixels
ah的8位数据中只有一个1,其余都是0,从最高位开始向低位移,当这个1移出(循环到最高位同时CF置位时)就表示下一像素应该写入下一字节
        dec     di
ah中的1未移出则还是当前字节,所以di减1,(因为stosb会使di加1)
CheckMorePixels:
       dec     cx
减计数器
       jz      AdvanceToNextScanLine
计数器为0则这行像素画完了
       mov     al,ah
       out     dx,al
切换位面
       mov     al,es:[di]
执行一下读操作,否则无法写入
       lodsb
再取出[si]中的值,这次是输出低4位,然后si自动加1,指向下1字节
       stosb
写入后1像素,同样是低4位有效,高4位抛弃
       ror     ah,1
准备切换到下一位面
       jc      CheckMorePixels2
判断是否ah中的1移出
       dec     di
ah中的1未移出则下一像素还是这个字节
CheckMorePixels2:
       loop    ColumnLoop
减计数器并循环

#19


对于16色显示模式,在写模式2中,A000段中每个字节对应8个像素,分别在8个位面上,不要把位面与二进制的位联系到一起。
256色比16色简单得多,每个字节就是一个像素,直接读写,只是需要切换显存映射窗口。

#20



cnzdgs谢谢你这几天坚持回帖,我发觉我看不懂VGA代码的关键在出现我理解不了。颜色值,像素值,位面,它们之间关系!
颜色值就是像素值吗,颜色值或像素值跟位面的关系?

#21


其实这些名词没有统一的定义,通常说的颜色值就是像素值,如果严格讲起来是这样:颜色值是由红、绿、蓝(R、G、B)三部分组成,每部分用1字节表示,取值范围是0~255;对于256色以下的图象数据,用索引值表示,索引值就是该像素的颜色在调色板中序号(基于0),显存中储存的也是索引值。
位面是从Plane翻译过来的,因为使用某些显示模式时,每个内存地址对应多个显存地址,所以把显存形象化地分成多个平面,每个内存地址在各个平面上都有一个投影,就是映射。至于“位”字,应该是来自于bit mark,在选择Plane是需要输出一个位掩码,每一位对应一个Plane,为1的表示选中,为0的表示屏蔽。

#22


哦,原来是这样啊,再次感谢。那么通过位掩码码选在一个位例如:bit mark:0001则选择位面0,我通过向该显存地址写一个字节,只有有这个位面才接受四个像素位(颜色),那么VGA通过"读"哪些位面来输入屏幕呢,而且读取是1位还是4位(这跟"读"操作有关吗)

#23


读的时候是4个位面,每个位面包含像素的一位,所以要分4次来读,每读一位切换一次位面,每次读出的数据中只有对应位是有效的,要自己把有效位提取出来。

#24


你真及时啊!!!学习了,从您上面的分析来看,bit mark包含在一字节中,是吧?

#25


#26


看了一天的代码,还是没什么收获。一会儿又是4位一个像素,一会儿又一字节一个像素。现在到好彻底的混为一谈了!!

#27


请问您能否在线聊天(WEB)。

#28


我看你是离线状态。
你只要知道:在16色显示模式下,读一个像素是从4个位面各读一位数据;写一个像素是向1个位面写一字节数据(低4位有效)就可以了。

#29


cnzdgs这几天都多亏你的帮忙,我才对VGA有了一定的了解。

#30


不客气。这方面还是要自己做程序去试才能掌握清楚,看别人的代码是比较费劲的。

#31


不知道是不是
CheckMoreScanLines:
        pop     di              ;get back the destination offset
        pop     cx              ;get back the width
        pop     ax              ;get back the left column's bit mask
        add     di,SCREEN_WIDTH_IN_BYTES  ;开始下一扫描线

#32


#33


.model small
 vgaseg equ 0a000h
sequen_ctl equ 3c4h
graphi_ctl equ 3ceh
Onscreen_X equ 20
Onscreen_y equ 100
linelen equ 640/8
only_one_pixel equ 003h
.code
start:

          mov ax,vgaseg  ;ES段指向显存地址0a000h
          mov es,ax
          mov  cx,0
          mov  dx,184Fh
          mov  bh,07
          mov  ax,600h
          int  10h
          
          ;没有堆栈
          
          mov ax, 12h
          int 10h
          
          mov ax,Onscreen_y
          push ax
          mov ax,Onscreen_X
          push ax
          call enable_write_mode_one
enable_write_mode_one proc
          mov bp,sp
          mov ax,[bp]   ;取x
          mov cl,3
          shr bx,cl    ;X0/8
          mov ax,[bp+2]
          mov cx,linelen
          mul cx
          add ax,bx           ;该坐标所在内存字节
          mov di,ax
          
          ;设置映象屏蔽寄存器
          mov dx,sequen_ctl
          mov al,2
          out dx,al
          inc dx
          mov al,0fh   ;四个位面全部充许写
          out dx,al
          ;选择写方式2
          mov dx,graphi_ctl
          mov al,5
          out dx,al
          inc dx
          mov al,2
          out dx,al
          ;计算屏蔽码,并设置位屏蔽寄存器
   point: dec dx
          mov al,8
          out dx,al         
          mov cx,[bp]  ;取出X在一个位面上位置
          and cl,7
          mov al,80h
          shr al,cl
          inc dx
          out dx,al   
          ror al,1
          jc done
          ;写数据
          mov ax,only_one_pixel    ;取颜色值
          mov ah,es:[di]    ;读一次使屏蔽寄存器有效
          mov es:[di],al ;写入显示存储器
          mov ax,[bp]
          inc ax
          mov [bp],ax
          jmp point
          
          done:nop
          jmp done
 
enable_write_mode_one endp
 end start

从POINT开始我的目的是显示一个点,但结果是一条线

#34


你这是一个循环,把jmp point去掉就是一个点了。另外
ror al,1
jc done
这两行应该去掉。

#35


啊,为什么,我按你所说的理解为:一个颜色,进行各位屏蔽后才写入位面,我的那个循环也是这个目的。我那理解错了!

#36


而且像素显示的位置也不对

#37


像素显示的位置这个问题解决了,堆栈处理不当

#38


设置位屏蔽寄存器只需要一次输出,输出的字节中为1的那一位对应位面被选中,其它位面都屏蔽。

#39


还有就是为什么要把X坐标除8,X不是已经是个字节(像素),还是它的这个像素概念是相对于位面???

假设颜色数值是003h(因为有8位,所以有8个像素),即二进制0000011。设x=1,那么x坐标求余为1,相反屏蔽了位6,那么剩下5位,我怎么办了,我通过循环也就是这个目的

#40


你现在理解的很混乱。
在写模式2下,A000段的每个字节对应的是8个像素,这8个像素各对应一个位面。在写像素之前要通过设置位屏蔽寄存器来控制选择其中1个位面,屏蔽其它7个位面。屏蔽寄存器中的8位二进制数中每1位对应一个位面,为1的位面有效,为0位面屏蔽。
当要写一个像素时,首先根据像素坐标(X,Y),先计算Y*640+X,这是总的像素序号,然后把Y*640+X除以8,商就是该像素在A000段的偏移量(将其保存到DI),余数是位面代号(将其保存到CL),把80H右移CL位,然后输出到位屏蔽寄存器,这样就选中了该像素对应的位面同时屏蔽了其它无关位面,然后把A000:DI的数据先读出一下,再把要写入的像素值写入,这样就完成了。

#41


我有两本VGA方面的电子版,说的都是很笼统,所以有时候分布清那是对的那是错的。。
1:你的意思是说一个页面有640*480个像素吗。
2:"余数是位面代号(将其保存到CL),把80H右移CL位",这只是单个位面吧,我的意思就是想使用XOR CL,1移动掩码位啊,就像我上面代码那样。

#42


1、这种显示模式整个屏幕就是640*480个像素;
2、写一个点就是要选一个位面,如果是画线才需要循环切换位面。

#43


总之,我还是谢谢你这几天回复,的确我被VGA各种操作搞混了,还是专注的看另一本书吧!:-)

#44




|--|                            ----------------------|
|1 |   0位面的1个字节                                   |
|  |     从最高位开始                                        |
|--|    每个时钟周期移动一个BIT                           |      
                                                        |   
|--|                                                    |
| 1|     1位面的1个字节                                         | 
|  |     从最高位开始                                           |
|--|    每个时钟周期移动一个BIT                             |
                                                          |   四位颜色值
                                                           |
|--|                                                      |
| 2|       2位面的1个字节                                     |
|  |     从最高位开始                                         |
|--|      每个时钟周期移动一个BIT                          |
                                                       |
|--|                                               |
|3 |        3位面的1个字节                            |
|  |       从最高位开始                               | 
|--|     每个时钟周期移动一个BIT     -------------|



迷糊了,怎么又成一个字节了啊

#45


是不是0~3位面相同位对应一个4位值

#46


跟时钟周期没有关系。

写的时候有8个位面,每个位面中的1字节(只有低4位有效)对应1个像素值。
读的时候有4个位面,4个位面中的同一位对应1个像素值的4位数据。

建议你先做一个写单一像素的函数,可以在屏幕上任意位置画一个点,然后再练习用画点的方式贴图;写单一像素熟悉了之后,再做一个画横线函数,可以在屏幕上任意位置画任意长度的横线,然后再练习画矩形;然后再做一个读单一像素的函数;再做读一个读横线函数,再练习读矩形。到这基本就都掌握了,可以再熟悉其它操作模式。