Giáo trình Đồ họa trong Pascal - Chương 1: Màn hình của máy tính - Bài 2: Truy cập vào bộ nhớ màn hình
Về nguyên tắc có 2 cách để truy cập bộ nhớ màn hình và các thanh ghi
+ Cách 1: Sử dụng các dịch vụ ngắt của ROM BIOS, ROM BIOS cung cấp tương đối đầy đủ các chương trình con ứng với các ngắt để phục vụ cho công việc này
+ Cách 2: Truy cập trực tiếp đưa vào các địa chỉ của các cổng (port) thông qua các ngôn ngữ lập trình
Cách 1 đơn giản tuy nhiên có nhược điểm tốc độ truy nhập chậm
I. Sử dụng các dịch vụ ngắt Int 10H của ROM BIOS
Dưới đây là một vài ngắt của ROM BIOS
1. Đưa một pixel vào bộ nhớ màn hình
input AH=0Ch
AL=màu
BH=trang
CX=x (cột)
DX=y (dòng)
Procedure writepixel(col,row,color: integer);
Var
v : Registers;
Begin
v.ah:=$0C;
v.al := color;
v.bh:=0;
v.cx:=col;
v.dx:=row;
intr($10,v);
End;
2. Đọc dữ liệu của một pixel
input AH=ODh
BH=trang
CX=x (cột)
DX=y (dòng)
output: AL=Color (giá trị màu của pixel)
Function readpixel(col,row: integer): integer;
Var
v : Registers;
Begin
v.ah:=$0D;
v.bh:=0;
v.cx:=col;
v.dx:=row;
intr($10,v);
readpixel:=v.al;
end;
3. Đọc dữ liệu từ thanh ghi màu (color Register)
Input: AH=10h
AL=15h
BX=n (số hiệu thanh ghi màu)
Output: CH=độ kích hoạt màu Green
CL=độ kích hoạt màu Blue
DH=độ kích hoạt màu Red
Type
RGB_COLOR_TYP = Record
red : Word;
green : Word;
blue : Word;
End;
Procedure read_color_reg(index: integer;Var color: RGB_COLOR_TYP);
Var
R : registers;
Begin
R.ax:=$1015;
R.bx:=index;
intr($10,R);
color.red:=R.dh;
color.green:=R.ch;
color.blue:=R.cl;
End;
và 4 bit màu đó lại nằm trong 4 byte ở cùng vị trí giống như ở bảng trên. Ta hãy xem lại nhóm 2 (Sequencer Register), trong nhóm này thanh ghi số 2 (Map Mask) sẽ xác định bit plane nào được truy cập:
Tên thanh ghi
Chỉ số thanh ghi
Địa chỉ ghi vào
Địa chỉ đọc ra
address
3C4
3C4
Map mask
2
3C5
3C5
Mỗi lần đưa số liệu vào bộ nhớ màn hình, 4 byte được đưa vào 4 bit plane. Thanh ghi Map Mask có nhiệm vụ xem bit plane nào được sửa đổi. Dạng của thanh ghi này như sau:
b3
b2
b1
b0
0001, 0010, 0100, 1000
để truy nhập vào một bit plane ta cần hai lệnh
Lệnh 1 : xác định thanh ghi cần truy nhập là thanh ghi mapmask
Lệnh 2 : đưa số liệu bit plane cần truy cập vào thanh ghi mapmask
Hai lệnh có dạng:
port[$3C4]:=2; (Vào thanh ghi cần truy cập)
port[$3C5]:= n; (đưa số liệu bitplan cần truy cập vào thanh ghi Mapmask)
(n=1,2,4,8)
Và ta cần 4 lần như vậy để truy cập vào 4 bit plane
Thủ tục ghi 8 điểm ra màn hình bằng 4 byte :
Procedure write_8_pixel(x,y: integer; Var b: four_byte);
Var
j,address: Integer;
Begin
port[$3c4]:=2;
address:=y*80+(x shr 3);
j:=1;
For i:=0 To 3 Do
Begin
port[$3c5]:=j;
Mem[buffer:address]:=b[i];
j:=j*2;
End;
End;
Để đọc trên màn hình trong chế độ 4 bit plane chúng ta phải đọc đồng thời 8 điểm, mỗi điểm gồm có 4 bit màu và 4 bit màu đó lại nằm trong 4 byte ở cùng vị trí. Ta chú ý đến nhóm thanh ghi thứ 4 (Graphics Control Register). Trong nhóm có thanh ghi mang chỉ số 4 (Read Map), thanh ghi này xác định bit plane nào sẽ được phép đưa số liệu ra :
Tên gọi thanh ghi
Chỉ số
Địa chỉ ghi
Địa chỉ đọc
Graphics address
3CE
3CE
Read Map
4
3cf
3cf
Để thực hiện phép đọc một bit plane nào đó chúng ta cần có hai lệnh:
Lệnh 1. Xác định số hiệu của thanh ghi trong nhóm cần truy nhập
Lệnh 2: Xác định số hiệu bitplane
Lệnh 1: Port($3ce):=4;
Lệnh 2: Port($3cf):=bitplane;
Và ta cần 4 lần như vậy để truy cập vào 4 bit plane
Type
four_byte = array[0..3] of Byte;
Thủ tục đọc 8 điểm trên màn hình vào 4 byte :
Procedure read_8_pixel(x,y: integer; Var b: four_byte);
Var
address,color: integer;
Begin
address:=y*80+(x shr 3);
port[$3ce]:=4;
for i:=0 To 3 Do
Begin
port[$3cf]:=i;
b[i]:=Mem[buffer:address];
End;
End;
Thủ tục vẽ một điểm : để vẽ một điểm trên màn hình ta đọc 4 byte liên quan đến điểm đó (4 byte ứng với 8 điểm có chứa điểm cần vẽ), mà vị trí của điểm cần vẽ trong 8 điểm đánh số từ 0 đến 7 là p=7-(x mod 8), sau đó ta thay bit thứ p của mỗi byte trong 4 byte đó bằng bit màu cần vẽ (bằng cách đổi màu cần vẽ điểm ra cơ số 2 ví dụ n=5 thì n=0101)
Procedure putdot(x,y,n: integer);
Var
p,m: byte;
ma: four_byte;
Begin
p:=7-(x mod 8);
m:=1;
For i:=1 To p Do m:=m*2;
For i:=0 To 3 Do
Begin
ma[i]:=(n shr i) mod 2;
ma[i]:=ma[i]*m;
End;
read_8_pixel(x,y,b);
For i:=0 To 3 Do
Begin
If ma[i]=0 Then b[i]:=b[i] And ($FF-m)
Else b[i]:=b[i] Or ma[i]; End;
write_8_pixel(x,y,b);
End;
Ví dụ 4: Ghi một điểm ảnh vào bộ nhớ màn hình ở chế độ 4 bit plane thông qua cổng.
Program Four_Bit_Planes;
Uses Crt,dos;
Const
buffer: Word=$A000;
gd: integer=0;
Type
four_byte = array[0..3] of Byte;
Var
gm,x,y,i: integer;
b: four_byte;
(**********************)
Procedure set_mode(mode: integer);
Var
R : registers;
Begin
R.ax:=$4f02;
R.bx:=mode;
intr($10,R);
End;
(**********************)
Procedure write_8_pixel(x,y: integer; Var b: four_byte);
Var
j,address: Integer;
Begin
port[$3c4]:=2;
address:=y*80+(x shr 3);
j:=1;
For i:=0 To 3 Do
Begin
port[$3c5]:=j;
Mem[buffer:address]:=b[i];
j:=j*2;
End;
End;
(**********************)
Procedure read_8_pixel(x,y: integer; Var b: four_byte);
Var
address,color: integer;
Begin
address:=y*80+(x shr 3);
port[$3ce]:=4;
for i:=0 To 3 Do
Begin
port[$3cf]:=i;
b[i]:=Mem[buffer:address];
End;
End;
(**********************)
Procedure putdot(x,y,n: integer);
Var
p,m: byte;
ma: four_byte;
Begin
p:=7-(x mod 8);
m:=1;
For i:=1 To p Do m:=m*2;
For i:=0 To 3 Do
Begin
ma[i]:=(n shr i) mod 2;
ma[i]:=ma[i]*m;
End;
read_8_pixel(x,y,b);
For i:=0 To 3 Do
Begin
If ma[i]=0 Then b[i]:=b[i] And ($FF-m)
Else b[i]:=b[i] Or ma[i];
End;
write_8_pixel(x,y,b);
End;
(**********************)
Begin
Set_mode($12);
y:=150;
for x:=0 To 639 Do
putdot(x,y,Magenta);
Readkey;
y:=300;
for x:=0 To 639 Do
putdot(x,y,Yellow);
Readkey;
Set_mode(3);
End.
Ví dụ 5: Đọc dữ liệu từ chế độ 4 bit plane thông qua các cổng của các thanh ghi
Đoạn chương trình sau mô tả cách đọc một điểm ảnh từ bộ nhớ màn hình
Program Read_Pixel_Color;
Uses Crt,dos,graph;
Const
buffer: Word=$A000;
gd: integer=0;
Var
gm,x,y: integer;
Function getdot(x,y: integer): integer;
Var
i,address,b,color: integer;
c: array[0..3] of integer;
Begin
address:=y*80+(x shr 3);
port[$3ce]:=4;
b:=x mod 8;
for i:=0 To 3 Do
Begin
port[$3cf]:=i;
c[i]:=Mem[buffer:address];
c[i]:=c[i] shl b;
c[i]:=c[i] shr 7;
End;
color:=c[0]+2*c[1]+4*c[2]+8*c[3];
getdot:=color;
End;
Begin
initgraph(gd,gm,'c:\tp\bgi');
directvideo:=false;
x:=320;y:=240;
putpixel(x,y,WHITE);
Writeln('Mau cua diem la : ',getdot(x,y));
readkey;
closegraph;
End.
3. Chế độ 640x480 256 màu (mode $101)
Ta xét trường hợp đọc/ghi vào bộ nhớ màn hình ở chế độ 256 màu theo chuẩn VESA (chú ý trong TP ứng với mode $101 độ phân giải 640x480, 256 màu) ở chế độ này bộ nhớ màn hình được tổ chức thành 5 mảng một chiều, mỗi mảng gồm 65536 phần tử (mỗi phần tử là 1 byte, mỗi byte ứng với 1 pixel) và do đó ta cần có 5 mảng đánh số từ 0 đến 4 để có thể chứa được 640x480 byte. Mỗi một mảng như vậy gọi là một Bank (dải), như vậy một bank 64K đúng bằng 64K mà bộ nhớ của máy chủ dành cho màn hình, mà có 5 bank như vậy (do đó bộ nhớ màn hình phải có ít nhất là 64K x 5 = 320K thì mới có thể đặt được chế độ này) và bộ nhớ của máy chủ chỉ có thể ánh xạ vào một bank của bộ nhớ màn hình và để cho bank (từ 0-4) nào được ánh xạ ta dùng thủ tục sau :
Procedure setbank(banknum: Integer);
Var R : Registers;
Begin
R.ax:=$4f05;
R.bx:=0;
R.dx:=banknum;
Intr($10,R);
End;
Một điểm có toạ độ (x,y) trên màn hình sẽ là điểm thứ linear_address:=y*640+x; Mà mỗi một bank có 65536 điểm nên điểm đó sẽ nầm ở bank thứ bank_num:=linear_address Div 65536; và địa chỉ của điểm đó ở bank này là pixel_offset:=linear_address Mod 65536;
Ví dụ 6:
Đoạn chương trình sau mô tả thủ tục đọc/ghi điểm ảnh ở chế độ 256 màu
Program VGA_256_Color;
Uses Crt,Dos;
Const
buffer: Word=$A000;
Var
v : Registers;
x,y,mode: Integer;
Procedure set_mode(mode: Integer);
Begin
v.ax:=$4f02;
v.bx:=mode;
Intr($10,v);
End;
Procedure setbank(banknum: Integer);
Begin
v.ax:=$4f05;
v.bx:=0;
v.dx:=banknum;
Intr($10,v);
End;
Procedure putdot(x,y: LongInt; color: Integer);
Var
linearaddress: LongInt;
banknum: Integer;
pixeloffset: Word;
Begin
linearaddress:=y*640+x;
banknum:=linearaddress Div 65536;
pixeloffset:=linearaddress Mod 65536;
setbank(banknum);
Mem[buffer:pixeloffset]:=color;
End;
Function getdot(x, y: LongInt): Integer;
Var
linearaddress: LongInt;
banknum,color: Integer;
pixeloffset: Word;
Begin
linearaddress:=y*640+x;
banknum:=linearaddress Div 65536;
pixeloffset:=linearaddress Mod 65536;
setbank(banknum);
getdot:=Mem[buffer:pixeloffset];
End;
Begin
clrscr;
mode:=$101; (* 640x480, 256 mau *)
set_mode(mode);
directvideo:=False;
x:=320; y:=240;
putdot(x,y,WHITE);
Writeln('Mau cua diem la : ',getdot(x,y));
Readkey;
mode:=3; (*80x25x16*)
set_mode(mode);
End.
Ví dụ 7: đọc nội dung thanh ghi màu thông qua cổng
Program Read_Write_Color_Registers;
Uses crt,graph,dos;
Const
COLOR_MASK=$3C6;
COLOR_REG_RD=$3C7;
COLOR_REG_WR=$3C8;
COLOR_DATA= $3C9;
Type
RGB_COLOR_TYP = Record
red : Word;
green : Word;
blue : Word;
End;
Procedure set_ mode(mode: integer);
Var
R : registers;
Begin
R.ax:=$4f02;
R.bx:=mode;
intr($10,R);
End;
Procedure write_color_reg(index: Integer;color: RGB_COLOR_TYP);
Begin
port[COLOR_REG_WR]:=index;
port[COLOR_DATA]:=color.red;
port[COLOR_DATA]:=color.green;
port[COLOR_DATA]:=color.blue;
End;
Procedure read_color_reg(index: integer;Var color: RGB_COLOR_TYP);
Begin
port[COLOR_REG_RD]:=index;
color.red:=port[COLOR_DATA];
color.green:=port[COLOR_DATA];
color.blue:=port[COLOR_DATA];
End;
Var
VIDEO_BUFFER : Word;
gd,gm,x,y,index: integer;
color: RGB_COLOR_TYP;
Begin
clrscr;
VIDEO_BUFFER:=$A000;
gm:=$13;
set_ mode(gm);
directvideo:=false;
index:=WHITE;
read_color_reg(index,color);
Writeln('3 mau cua thanh ghi mau WHITE la: ');
Writeln('Red: ',color.red,'; Green: ',color.green,'; Blue: ',color.blue);
Readkey;
index:=BLUE;
color.red:=63;
color.green:=0;
color.blue:=63;
write_color_reg(index,color);
(* Ve duong thang ngang *)
y:=100;
for x:=0 To 319 Do
Mem[VIDEO_BUFFER:y*320+x]:=index;
Readkey;
gm:=3; (* Text Mode *)
set_graph_mode(gm);
End.
Ví dụ 8: làm màn hình sáng tối dần dần
Program Display;
Uses crt,dos;
Const
COLOR_MASK=$3C6;
COLOR_REG_RD=$3C7;
COLOR_REG_WR=$3C8;
COLOR_DATA= $3C9;
b: Integer = 1;
type
RGB_COLOR_TYP = Record
red : Word;
green : Word;
blue : Word;
End;
Procedure set_mode(mode: integer);
Var
R : registers;
Begin
R.ax:=$4f02;
R.bx:=mode;
intr($10,R);
End;
Procedure write_color_reg(index: Integer;color: RGB_COLOR_TYP);
Begin
port[COLOR_REG_WR]:=index;
port[COLOR_DATA]:=color.red;
port[COLOR_DATA]:=color.green;
port[COLOR_DATA]:=color.blue;
End;
Var
VIDEO_BUFFER : Word;
gm,index,n: integer;
color: RGB_COLOR_TYP;
Begin
clrscr;
VIDEO_BUFFER:=$A000;
gm:=$13;
set_mode(gm);
index:=0;
n:=2;
Repeat
n:=n+b;
color.red:=n;
color.green:=0;
color.blue:=n;
write_color_reg(index,color);
Delay(150);
If n>60 Then b:=-1;
If n<4 Then b:=1;
Until KeyPressed;
gm:=3; (* Text Mode *)
set_mode(gm);
End.
File đính kèm:
BAI2.doc



