CHƯƠNG 9 TRUY NHẬP TRỰC TIẾP VÀO BỘ NHỚ Trong chương này trình bầy các vấn đề: + Hai kiểu địa chỉ: Địa chỉ phân đoạn và địa chỉ thực + Truy nhập tới địa chỉ phân đoạn + Đổi từ địa chỉ ph
Trang 1CHƯƠNG 9 TRUY NHẬP TRỰC TIẾP VÀO BỘ NHỚ
Trong chương này trình bầy các vấn đề:
+ Hai kiểu địa chỉ: Địa chỉ phân đoạn và địa chỉ thực
+ Truy nhập tới địa chỉ phân đoạn
+ Đổi từ địa chỉ phân đoạn sang địa chỉ thực
+ Bộ nhớ màn hình, truy nhập trực tiếp vào bộ nhớ màn hình
+ Dùng con trỏ để lấy dữ liệu từ bộ nhớ phân đoạn
+ Dùng con trỏ hàm để thực hiện các thủ tục của DOS
§ 1 CÁC HÀM TRUY NHẬP THEO ĐỊA CHỈ PHÂN ĐOẠN
1 Hàm pokeb: Gửi một ký tự vào bộ nhớ.
+ Nguyên mẫu trong dos.h như sau:
void pokeb(unsigned seg, unsigned off, char value);
+ Công dụng: Gửi giá trị ký tự value vào bộ nhớ tại địa chỉ phân
đoạn seg:off
2 Hàm peekb: Nhận một ký tự từ bộ nhớ.
+ Nguyên mẫu trong dos.h như sau:
char peekb(unsigned seg, unsigned off);
+ Công dụng: Nhận một byte tại địa chỉ phân đoạn seg:off
3 Hàm poke: Gửi một số nguyên vào bộ nhớ.
+ Nguyên mẫu trong dos.h như sau:
void poke(unsigned seg, unsigned off, int value);
+ Công dụng: Gửi giá trị nguyên value vào bộ nhớ tại địa chỉ
phân đoạn seg:off
4 Hàm peek: Nhận một số nguyên từ bộ nhớ.
+ Nguyên mẫu trong dos.h như sau:
int peek(unsigned seg, unsigned off);
+ Công dụng: Nhận một word tại địa chỉ phân đoạn seg:off
5 Hàm movedata: Sao các byte.
+ Nguyên mẫu trong mem.h như sau:
void movedata(unsigned seg_gui, unsigned off_gui,
unsigned seg_nhan, unsigned off_nhan, int n); + Công dụng: Sao n byte từ seg_gui:off_gui đến
seg_nhan:off_nhan
§ 2 BỘ NHỚ MÀN HÌNH VĂN BẢN 2.1 Cách biểu diễn ký tự trong bộ nhớ màn hình
Bộ nhớ màn hình văn bản bắt đầu từ địa chỉ : (0xb800:0x0000)
Khi đưa một ký tự vào vùng nhớ màn hình, thì nó sẽ hiện lên màn hình Mỗi ký tự trên màn hình chiếm 2 byte trong bộ nhớ màn hình: byte đầu chứa mã ASCII, byte thứ hai biểu diễn mầu hiển thị gọi là byte thuộc tính Các bit của byte thuộc tính:
B7B6B5B4B3B2B1B0 được chia làm 3 nhóm:
+ Nhóm 1 gồm bit B7 biểu thị sự nhấp nháy Nếu B7=0 thì ký tự không nhấp nháy, nếu B7=1 thì ký tự sẽ nhấp nháy
+ Nhóm 2 gồm các bit B6, B5 và B4 Các bit này chứa được một
số nguyên từ 0 đến 7 và biểu thị 8 mầu nền của ký tự
+ Nhóm 3 gồm các bit B3, B2, B1 và B0 Các bit này chứa được một số nguyên từ 0 đến 15 và biểu thị 16 mầu của ký tự
2.2 Trang màn hình
Trang 2Mỗi trang màn hình gồm 80x25 ký tự, do đó cần 80x25x2=4000
byte bộ nhớ Thực tế mỗi trang màn hình được phân bố 4096 =
0x1000 byte Như vậy 4 trang màn hình được phân bố như sau:
+ Trang màn hình thứ 0 bắt đầu từ địa chỉ 0xB800:0x0000
+ Trang màn hình thứ 1 bắt đầu từ địa chỉ 0xB800:0x1000
+ Trang màn hình thứ 2 bắt đầu từ địa chỉ 0xB800:0x2000
+ Trang màn hình thứ 3 bắt đầu từ địa chỉ 0xB800:0x3000
2.3 Chọn trang hiển thị
Tại mỗi thời điểm chỉ có thể hiển thị được một trong 4 trang màn
hình Để hiển thị trang màn hình thứ t (t=0,1,2,3) chúng ta sử dụng
chức năng 5 của ngắt 0x10 theo mẫu sau:
union REGS v,r;
v.h.ah = 5 ; // Chức năng 5
v.h.al = t ; // Số hiệu trang màn hình cần hiển thị
int86(0x10, &v, &r); // Thực hiện ngắt 0x10
2.4 Ví dụ minh hoạ
Ví dụ sau dùng hàm pokeb để đưa các ký tự vào các trang của bộ
nhớ màn hình, sau đó dùng chức năng 5 của ngắt 0x10 để chọn trang
hiển thị
//CT9_03.CPP
#include <dos.h>
#include <conio.h>
char d1[]={'C',1*16+14,'H',1*16+14,'U',1*16+14,'C',1*16+14};
char d2[]={'M',2*16+15,'U',2*16+15,'N',2*16+15,'G',2*16+15};
void main()
{
union REGS v,r;
clrscr();
//Mặc định hiển thị trang 0 for (int i=0;i<8;++i) pokeb(0xb800,i,d1[i]);
getch();
//Hien thi trang 1 v.h.ah = 5 ; v.h.al = 1 ; int86(0x10,&v,&r);
for (i=0;i<8;++i) pokeb(0xb800,0x1000+i,d2[i]);
getch();
//Hien thi trang 0 v.h.ah = 5 ; v.h.al = 0 ; int86(0x10,&v,&r);
getch();
//Hien thi trang 1 v.h.ah = 5 ; v.h.al = 1 ; int86(0x10,&v,&r);
getch();
}
§ 3 CHUYỂN ĐỔI ĐỊA CHỈ 3.1 Để chuyển từ địa chỉ thực sang địa chỉ phân đoạn ta dùng các macro:
unsigned FP_SEG(địa_chỉ_thực) unsigned FP_OFF(địa_chỉ_thực)
3.2 Để chuyển từ địa chỉ phân đoạn sang địa chỉ thực ta dùng macro:
void far *MK_FP(seg,off)
Trang 3Ví dụ 1 Sau khi thực hiện các câu lệnh:
char buf[100];
unsigned ds,dx;
ds = FP_SEG(buf); dx = FP_OFF(buf);
thì ds:dx chứa địa chỉ của mảng buf
Ví dụ 2 Sau khi thực hiện các câu lệnh:
char far *pchar;
pchar = (char far*)MK_FP(0xb800:0);
thì pchar trỏ tới địa chỉ đầu của bộ nhớ màn hình Khi đó ta có thể sử
dụng các lệnh gán để truy nhập trực tiếp tới bộ nhớ màn hình
§ 4 CÁC VÍ DỤ MINH HOẠ Chương trình 1 Chương trình minh hoạ cách truy nhập trực tiếp
vào bộ nhớ màn hình có địa chỉ đầu là 0xB800:0 Chương trình gồm
hàm main() và hai hàm sau:
1 Hàm cuaso
void cuaso(int dongt,int cott,int dongd,int cotd,int maucs);
thiết lập một cửa sổ mầu có toạ độ góc trên-trái là (dongt, cott) và
góc dưới-phải là (dongd,cotd) Mầu cho bởi tham số maucs Ở đây
sử dụng hàm pokeb và địa chỉ phân đoạn
2 Hàm duarmh
void duarmh(char *day, int dong, int cotd, int cotc,int m_nen,
int m_chu);
sẽ đưa ra màn hình một dẫy ký tự (chứa trong dãy) tại dòng dong, từ
cột cotd đến cotc Mầu nền cho bởi m_nen, mầu chữ cho bởi m_chữ
ở đây sử dụng toán tử gán trên địa chỉ thực
Trong hàm main() sẽ sử dụng các hàm cuaso và duarmh để tạo hai
cửa sổ và viết hai dòng chữ trên trang màn hình thứ hai (từ dòng 26
đến dòng 50)
/*
chương trình minh hoạ cách truy nhập trực tiếp vào bộ nhớ của màn hình
*/
#include "dos.h"
#include "conio.h"
void duarmh(char *day, int dong,I nt cotd, int cotc,I nt m_nen,
int m_chu);
void cuaso(int dongt,int cott,int dongd,int cotd,int maucs);
main() { cuaso(26,1,50,80,BLUE);
duarmh("Chuc mung nam moi", 28, 30, 50, MAGENTA,
WHITE);
cuaso(30,20,46,60,RED);
duarmh("Chuc mung nam moi", 40, 30, 50, MAGENTA,
YELLOW);
getch();
} void cuaso(int dongt,int cott,int dongd,int cotd,int maucs) /* Dung dia phan doan */
{ int i, j, p, t, dt, dd, mau;
union REGS v, r;
/* Xac dinh thuoc tinh mau */
mau = (maucs << 4)+maucs;
/*
Xac dinh trang man hinh t
va cac chi so dong tren dt, dong duoi dd
Trang 4trong trang t
*/
t=(dongt-1)/25;
dt=(dongt-1)-t*25; dd=(dongd-1)-t*25;
/* Chon t la trang hien thi */
v.h.ah=5;v.h.al=t; int86(0x10,&v,&r);
/*
Dua cac khoang trong (ma 32) va thuoc tinh mau
vao cac vi tri thich hop cua bo nho man hinh
*/
for (i=dt;i<=dd;++i)
{
p=t*4096+i*160+(cott-1)*2;
for (j=0;j<=cotd-cott;++j)
{
pokeb(0xb800,p+2*j,32);
pokeb(0xb800,p+2*j+1,mau);
}
}
}
void duarmh(char *day, int dong, int cotd, int cotc, int m_nen,
int m_chu) /* Dung dia chi thuc */
{
int i,p,t,d,kt,mau;
char far *buf;
union REGS v,r;
/* Lay dia chi thuc cua bo nho man hinh */
buf=(char far*)MK_FP(0xb800,0);
/* Xac dinh thuoc tinh mau */
mau = (m_nen << 4)+m_chu;
/*
Xac dinh trang man hinh t
va cac chi so dong d trong trang t
*/
t=(dong-1)/25; d=dong-1-t*25;
/* Chon t la trang hien thi */
v.h.ah=5;v.h.al=t; int86(0x10,&v,&r);
p=t*4096+d*160+(cotd-1)*2;
/*
Dua cac ky tu va thuoc tinh mau vao cac vi tri thich hop cua bo nho man hinh
*/
for (i=0;i<=cotc-cotd;++i) {
if ((kt=day[i])==0) break;
buf[p+2*i]=kt;
buf[p+2*i+1]=mau;
} }
Chương trình 2 Biết địa chỉ của các thủ tục xử lý ngắt được lưu
trữ trong bộ nhớ từ địa chỉ 0000:0000 đến 0000:0x0400 Chương trình sẽ cho biết địa chỉ của thủ tục xử lý ngắt n (giá trị n nhập vào từ bàn phím) Số hiệu của ngắt được tính từ 0, nhưng n được đánh số
từ 1
/*
Trang 5Xac dinh dia chi cac thu tuc ngat */
#include "dos.h"
#include "conio.h" #include "stdio.h"
main()
{
unsigned char far *p; /*p se tro toi bang vecto ngat*/
int n; /* n - so hieu ngat, n=1,2, */
int k; /* vi tri cua ngat n trong bang vecto ngat */
unsigned seg,off;
/* p tro toi bang vecto ngat */
p=(unsigned char far*)MK_FP(0,0);
clrscr();
while(1)
{
printf("\n So hieu ngat (Bam 0 - Ket thuc): ");
scanf("%d",&n); if(n==0) break;
k=(n-1)*4;
off=p[k]+256*p[k+1]; seg=p[k+2]+256*p[k+3];
printf("\nDia chi %x:%x",seg,off);
}
}
Chương trình 3 Chương trình minh hoạ cách dùng con trỏ hàm
để thực hiện thủ tục khởi động lại máy của DOS, biết địa chỉ đầu của
thủ tục này là 0xFFFF:0000 Chương trình yêu cầu nhập mật khẩu
Nếu chọn đúng (bấm ABCD và Enter) thì chương trình tiếp tục làm
việc, nếu vào sai thì sẽ khởi động lại máy
#include <dos.h>
#include <conio.h>
#include <iostream.h>
#include <ctype.h>
typedef void far (*HAM)(void);
void khoi_dong_may(void) {
HAM f;
f = (HAM)MK_FP(0xFFFF,0);
f();
} char mat_khau[]= {'A','B','C','D'};
int n = sizeof(mat_khau)/sizeof(char);
void main() {
char i, ch, sai_mat_khau;
clrscr();
i=0;
sai_mat_khau=0;
cout << "\nMat khau: ";
while(1) { ch=getch();
if (ch==13) break;
cout << '*' ;
if (i<n) {
if (toupper(ch)!=mat_khau[i]) sai_mat_khau=1;
Trang 6else
sai_mat_khau=1;
++i ;
}
if (sai_mat_khau)
{
cout << "\nSai mat khau, Khoi dong lai may";
getch();
khoi_dong_may();
}
else
{
cout << "\nDung mat khau, tiep tuc chuong trinh";
getch();
}
}
Chương trình 4 Chương trình minh hoạ cách dùng biến con trỏ
để lấy dữ liệu về thời gian hệ thống chứa trong 4 byte bắt đầu từ địa
chỉ 0:0x46C Chương trình cũng minh hoạ cách truy nhập trực tiếp
bộ nhớ màn hình văn bản (địa chỉ đầu là 0xB800:0) và cách bắt phím
tổng quát Chương trình sẽ in ra màn hình các chữ cái một cách ngẫu
nhiên Khi bấm phím F1 chương trình tạm dừng để thông báo thời
gian Để kết thúc chương trình bấm phím ESC
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#define VT 132 // vi tri thong bao
//Dia chi vung nho man hinh char far *p_mh = (char far*)MK_FP(0xB800,0) ; //Dia chi 4 byte chua thoi gian
unsigned long far *t_time=(unsigned long far*)MK_FP(0,0x46C);
char buf_time[]={'T',47,'I',47,'M',47,'E',47,':', 47, 32, 47, 32, 47,
32, 47, 32, 47, 32, 47, 32, 47, 32, 47, 32, 47, 32, 47};
char buf_luu[28];
void thong_bao_thoi_gian() {
//Luu trang thai man hinh for (int i=0; i<28; ++i) buf_luu[i]=p_mh[i];
// Xac dinh gio, phut, giay int gio = (int)(*t_time/65543) ; unsigned long du = *t_time%65543 ; int phut = (int)(du/1092);
du = du%1092;
int giay = (int)(du/18);
//Doi ra ky tu dua vao mang buf_time buf_time[12]=gio/10 + 48;
buf_time[14]=gio%10 + 48;
buf_time[18]=phut/10 + 48;
buf_time[20]=phut%10 + 48;
buf_time[24]=giay/10 + 48;
buf_time[26]=giay%10 + 48;
//Dua thong bao goi ra man hinh for (i=0; i<28; ++i)
Trang 7p_mh[i] = buf_time[i];
getch();
//Khoi phuc man hinh
for (i=0; i<28; ++i)
p_mh[i] = buf_luu[i];
}
void main()
{
int ch1, ch2;
clrscr();
while(1)
{
if (kbhit())
{
ch1=getch();
if (ch1==0) ch2=getch();
if (ch1==27) //ESC
break;
if (ch1==0 && ch2==59) // Bam F1
thong_bao_thoi_gian();
}
//In cac chu cai mot cach ngau nhien
gotoxy(random(80)+1,random(25)+1);
putch(random(26)+65);
delay(400);
}
}
503