中山大學 West BBS-西子灣站

『主選單』
分類佈告
分類精華
休閒聊天
個人郵件
個人設定
重新登入
『應數系組合語言(楊昌彪老師)』一般區佈告
←回列表  ↑上一篇  ↓下一篇        張貼  回應       
發信人: Ribber (小癟三)    看板:math-asm
日期: Mon Jul  7 13:38:02 1997
標題: 組合語言期未報告
來源: 中山大學 Formosa BBS Client


組合語言期末報告
學生:楊正
學號:8342033

一.前言:
這一次的組合語言期末作業,我所做的是一個簡易的打字遊戲,這個遊戲
的大意是:有一個人在走一個橋,如果此人沒有在預定的時間之內打下橋下的
字元的話,橋就會斷掉,最後,就會跌入河中死亡.
二.使用技巧:
這一個程式所使用的技巧大致有下:
1. 彩色顯示:由於我的程式均以純文字的方式來表現,所以,在我的組語部份
所用來顥示人與橋與文字的部份,均放入 b800:0000的位置,比較值得注意
的是,由於我的程式之中,每次程式在重畫的時候,由於重畫的部份範圍非
常的小,所以後來我並沒有開一個Buffer來放這個些變動的資訊,相反地,
我在DataSegment的地方則是保留了 CurBrg,CurTxt,CurCsr等三個變數以記
載現在橋斷在那裡,現在文字已打到那一個位置,現在的人在那一個位置等,
而每次發生重畫的現象之時,就直接地推出應該要畫的位置而只接將其寫入
記憶體進去.
2. INT 1CH的中斷:這個地方是我覺得整個程式最為麻煩的地方,為了這個
寫這個部份,我的電腦當了好幾次,原來我本來是想時間的控制交由C語
言來處理,可是,事實上是根本不可行的,C語言可以做到“過1秒後才
執行…"但無法做到”每1秒就執行…“因此,我就藉用在課本上截下1CH
的技巧來處理,可是,事實上,這個地方比我想像的問題還多,例如:在
我的程式之中,新1CH的程式是每一秒就會作一次的判斷,每過1至6秒
之間就會發生一次重畫的現象,可是,事實上,在整個的程式執行的當中,
不是事事刻刻地都要有這種現象,因此,在某個時候,例如:換局的時候,
結束的時候等,這些時候都應該要重新回復原來1CH的面貌.原來我在寫
這個部份的時候,一直處理不佳,以致於經常動不動就當.
3. C語言和組語的呼叫:這個部份是最基本的.不過,在C與組語之間的傳遞
參數之中,我只用了最簡單的傳char,老實說,我本來是想使用傳址呼叫來
傳遞整個字串,可是一直不幸地失敗了!所以最後還用了最簡單的利用AH
來傳遞一char的.
4. 另外,還有一些有關系統時間的得到以及改中斷向量表,查得中斷向量表等
技巧,這個內容其實在我的 Macro.h檔之中就可以得知.

三.程式內容:
以下是我的程式內容:
1.prjc.c:(C程式部份)
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<time.h>
#include<dos.h>

extern void initial(void);
extern char ToCheck(char);  //測試是否為正確字元的函數,若正確,則傳回1,
                              //  不然就傳回0
extern void TimeInit(void);  //時間初始化函數
extern void TimeBack(void); //恢復1ch的函數

char OldSec,PerChar=6;
int cl;            //cl所指的是目前的Class種類,其中cl=1代表可以出現所有可
                  //可輸入的ASCII碼,例如:!!@#$ASFD等,而cl=2則代表只
                  //有大寫的英文字母而已

char PerCh()     //傳回目前到底是處在那一個Level(每幾秒換一次)的階段
{
 return(PerChar);
}

char rndlist(void)   //此副程式是能隨機地產生一個"字元"
{
  if(cl==1)
     return( random(95) + 32 );  //可輸入的ASCII碼
  else
    return( random(26) + 65 );  //只有英文字母
}

void ToTest(void)  //作每一次輸入的測試,不過,其實真正的測試工作仍是在
{                   //組合語言的那一端來做的
  char ch,temp,cur=0;
    do {
       ch = getch();   //等在抓取輸入的字元

       cur=cur+ ToCheck(ch); //cur代表目前cursor所處在的位置

       } while (cur<60 && ch!=3); //當完全完成或按Ctrl-C後,便跳離迴圈
  if(ch==3)   //若為 Ctrl-C則,代表離開
  {
   TimeBack();  //恢復原來的ich
   exit(0);
  }
}


void main(void)  //主程式
{
  char i,j;

  clrscr();   //清除瑩幕

//以下是為了要選擇要從每分鐘多少字開始執行起
  printf("* Level Menu........\n");
  printf(" (1) 10 char per minutes.\n");
  printf(" (2) 12 char per minutes.\n");
  printf(" (3) 15 char per minutes.\n");
  printf(" (4) 20 char per minutes.\n");
  printf(" (5) 30 char per minutes.\n");
  printf(" (6) 60 char per minutes.\n");

  printf("Plase input your Level:");
  scanf("%d",&i);

//以下是為了要選擇Class種類

  printf("* Class Menu........\n");
  printf(" (1) All typeabel ASCII .\n");
  printf(" (2) Only Captial English Letter.\n");

  printf("Plase input your Class:");
  scanf("%d",&cl);

  printf("Please Type a Key to Begin .......\n");
  getche();

  randomize(); //重新搖亂數種子
  for(PerChar=6-i+1;PerChar>=1;PerChar--)
  {
   initial();   //初始化 (畫 橋,人,文字) 等
   sleep(1);
   TimeInit(); //截下1ch
   ToTest();  //呼叫上面的程式
   TimeBack(); //回復1ch
  }
}
2.prjasm.asm:(assmble部份)(以下有部份亂碼,實在是因為有些是128之後的
ASCII碼所致:
include macro.h
		.model  small

		extrn   _rndlist : near
		extrn   _PerCh: near
		public  _initial        ;Inicialize Program.......
		public  _ToCheck        ;To Check if right .......
		public  _TimeInit       ;To Inicialize Time ......
		public  _TimeBack       ;To Back Time ............

;Following are the Data Segment
		.data
; The Definition is for Color
B       equ     0fh
L       equ     01h
G       equ     02h
R       equ     04h
W       equ     07h
Y       equ     0eh

;Declare Level and How muck time to Change again
_level  db      6                ; Level = A Char Cost
_permin db      10               ; Per_Min = 60 / Level

;Declare Current Text, Current Bridge, Current Cursor :"Offset of B800"
_CurTxt dw      2260d
_CurBrg dw      2100d
_CurCsr dw      1940d

;Declare Current Cursor Location :座標
_LocCsr db      12d,10d
;Declare End Program Cursor should be located where
_EndCsr db      23d,0d
;Declare the Die Message
_SayBye db      'Wo! Wo! Poor Guy, You Have Died..... ',0dh,0ah,'$'

;Define the table to find Current PerMin
_Levels db      '60','30','20','15','12','10'

;Declare the Monitor Buffer
_pic1   label   byte             ;B800:
   ;00000000001111111111222222222233333333334444444444555555555566666666667777777777
   ;01234567890123456789012345678901234567890123456789012345678901234567890123456789
db '                                                                                ';// 0:0000
db '\ | /         \/    ____              >.<                                       ';// 1:00A0
db '- O - ......                     ><                \/                           ';// 2:0140
db '/ | \           ====                                                            ';// 3:01E0
db '                                                                                ';// 4:0280
db 'Current Level:     Char per Min                                                 ';// 5:0320
db 'Current Class:                                                                  ';// 6:03C0
db '                                                                                ';// 7:0460
db '                                                                                ';// 8:0500
db '       /^\                                                            /^\       ';// 9:05A0
db '       眷?                                                           眷?      ';//10:0640
db '       偽?                                                           偽?      ';//11:06E0
db '      ?                                                                 ?     ';//12:0780
db '     偽偽?-----------------------------------------------------------偽偽?    ';//13:0820
db '    袍EXT:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz12345678陶陶陶    ';//14:08C0
db '   |||||||                                                            |||||||   ';//15:0960
db '   HHHHHHH                                                            HHHHHHH   ';//16:0A00
db ' ==偽偽偽?                                                           偽偽偽?= ';//17:0AA0
db ' ==IIIIIII^^^  ^ ^^^ . ^^ ^^^..^^^...^^^. ^^ ..^^^  ^^^^.^^.^^^^^  .^^IIIIIII== ';//18:0B40
db ' ==IIIIIII   ^^^  ^ ^^^ . ^^ ^^^..^^^...^.^.^^^.^^^.^^.^^^^.^^.^^^^^.^IIIIIII== ';//19:0BE0
db '                                                                                ';//20:0C80
db '  ___________________________________________________________________________   ';//21:0D20
db '                                                                                ';//22:0DC0
db '                                                                                ';//23:0E60
db '                                                                                ';//24:0F00

;用來定義相對應的屬性碼
_pic2   label   byte             ;B800:
db  80 dup(W);// 0:0000
db  Y,W,Y,W,Y,W,W,W,W,W,W,W,W,W,5,5,W,W,W,W,G,G,G,G,G,55 dup (W) ;// 1:00A0
db  Y,W,B,W,Y,W,G,G,G,W,G,G,W,W,W,65 dup (G) ;// 2:0140
db  15 dup (Y),65 dup(R) ;// 3:01E0
db  80 dup (W) ;// 4:0280
db  80 dup (B) ;// 5:0320
db  14 dup (W),4 dup(08fh),62 dup(W) ;// 6:03C0
db  14 dup (W),4 dup(08fh),62 dup(W) ;// 7
db  80 dup (W)
db  80 dup (L) ;// 9:05A0
db  80 dup (R) ;//10:0640
db  80 dup (5) ;//11:06E0
db  80 dup (B) ;//12:0780
db  80 dup (B) ;//13:0820
db  80 dup (W) ;//14:08C0
db  80 dup (Y) ;//15:0960
db  80 dup (R) ;//16:0A00
db  80 dup (9) ;//17:0AA0
db  10 dup (8), 60 dup (93h), 10 dup (8);//19:0BE0
db  10 dup (6), 60 dup (93h), 10 dup (6);//19:0BE0
db 160 dup (B) ;//21:0D20
db 240 dup (W) ;//24:0F00

;以下為Code Segment ,不過亦有些Data 是定義在此的
		.code
;放真正1Ch的Offset:Segment
_1COft  dw      00h
_1CSeg  dw      00h

_SecSum db      00h     ;存放目前秒數已累積至多少
_LstSec db      00h     ;存放上一次的科數是多少
_PerChr db      06h     ;放現在的Level是每多少秒變一次


;====================================新的1CH的副程式===================================
_New1CH         proc

		@pushall        ;將所有的暫存器內容給保存起來(Macro)

		mov             ax,0b800h  ;將es改為B800(瑩幕所顯示的相對應Segment)
		mov             es,ax

		@GetTime        ;抓取目前系統時間(Macro)

		cmp             cs:_LstSec,dh    ;看秒數到底變了沒有
		jz              endshow          ;如果變了,就重秀出秒,不然,就不做任何事

		mov             cs:_LstSec,dh

		; If the Second is different ..........

		; (1) Show it
		@pushall

		@ShwBCDNum      ch ,090h
		@ShwChar        ':',094h
		@ShwBCDNum      cl ,096h
		@ShwChar        ':',09ah
		@ShwBCDNum      dh ,09ch

		@popall

		; (2) Add SecSum
		mov             ah,cs:_SecSum
		inc             ah

		;   1. If SecSum < PerChr then SecSum <= AH
		;      else SecSum <= 0
		cmp             ah,cs:_PerChr
		jnz             label6

		mov             ah,00h
		mov             cs:_SecSum,ah


;                @pushall

;                @ShwBCDNum      ch ,090h
;                @ShwChar        ':',094h
;                @ShwBCDNum      cl ,096h
;                @ShwChar        ':',09ah
;                @ShwBCDNum      dh ,09ch

;                @popall


		;  (1) To Change Bridge

		mov     ax,@data
		mov     ds,ax

		mov     ax,0b800h
		mov     es,ax

		lea     bx,ds:_CurBrg           ;
		mov     dx,[bx]                 ;
		mov     bx,dx

		mov     dh,20h                  ;
		mov     es:[bx],dh              ;
		mov     dh,08fh                 ;
		mov     es:[bx+3],dh            ;

		mov     dx,bx                   ;
		add     dx,2                    ; CurBrg=CurBrg + 2
		lea     bx,ds:_CurBrg           ;
		mov     [bx],dx                 ;

		;去查看是否到了死的地步
		call            _ToCheckIfDie


		jmp             endshow
label6:
		mov             cs:_SecSum,ah


endshow:
		@popall

		jmp             dword ptr cs:_1Coft


		iret
_New1CH         endp


;=======================================時間初始化副程式 ============================
_TimeInit       proc
;將暫存器內容給保存起來
		push    ax
		push    bx
		push    cx
		push    dx
		push    ds
;抓取系統時間當以設定LstSec的初值
		@GetTime
		mov     cs:_LstSec,dl
;查得1ch的中斷向量表
		@GetINT 1ch
;存放1ch的位置於1coft,1cseg
		mov     cs:_1COft,bx
		mov     cs:_1CSeg,es

		lea     dx,_New1CH
		push    cs
		pop     ds
;更改1ch的中斷向量內容
		@ChgINT 1ch

		pop     ds
		pop     dx
		pop     cx
		pop     bx
		pop     ax

		ret
_TimeInit       endp

;===================================回復1ch中斷向量內容函式==============================
_TimeBack       proc

		@pushall
;把1ch 的內容從 1coft,1cseg中重新拿出
		lea     bx,_1Coft
		mov     dx,cs:[bx]
		lea     bx,_1Cseg
		mov     ax,cs:[bx]
		push    ax
		pop     ds
;更改(Macro)
		@ChgINT 1ch

		@popall

		ret
_TimeBack       endp

;=================================== 測試是否為死亡的 函式 =========================
_ToCheckIfDie   proc

		@pushall

		mov     ax,@data
		mov     ds,ax

;比較ax=CurCsr和dx=CurBrg-160的位置
		lea     bx,ds:_CurCsr
		mov     ax,[bx]
		lea     bx,ds:_CurBrg
		mov     dx,[bx]
		sub     dx,160d

		; If _CurCsr >= _CurBrg then NoDie
		cmp     ax,dx
		jge     NoDie

		; else Die......
		; (1) Time Back
		call    _TimeBack

		; (2) Show Die

		@SetCsr _LocCsr               ;更換目前的遊標位置
		@PrintChar      20h           ;把原來的人消失(印成空白)(Macro)

		mov     ah,18d
		mov     ds:_LocCsr,ah

		@SetCsr _LocCsr               ;更換目前的遊標位置於水中
		@PrintChar      1h            ;把人印出在水之中
		@SetCsr _EndCsr
		@PrintStrn      _SayBye       ;印出"你死了….."

		@EndRun                       ;利用 ah=4ch,int21h 來結束程式

NoDie:
		@popall
		ret
_ToCheckIfDie   endp


;=================================此程式用來檢查輸入字元和要求文字是否相符===================
_ToCheck        proc

		push    bp
		mov     bp,sp

		push    bx
		push    dx
		push    ds
		push    es

		mov     ax,@data
		mov     ds,ax

		mov     ax,0b800h
		mov     es,ax                   ;Set ES as B800h


		mov     bx,offset _CurTxt       ; BX = Current Text Location
		mov     dx,[bx]
		mov     bx,dx
		mov     al,byte ptr es:[bx]     ; AL = Current Text

;                mov     dl,al
;                mov     ah,02h
;                int     21h


		mov     dx,[bp + 4]             ; DL = Input Parameter

;                mov     ah,02h
;                int     21h

		sub     al,dl                   ; AL = AL – DL
		jnz     label1

		;If the input is right then
		; 1.To Change Picture
		;  (1) To Change CurTxt Color to Gray and CurTxt + 2
		mov     ah,8                    ; Set to Gray
		mov     es:[bx+1],ah            ;
		mov     ah,01fh
		mov     es:[bx+3],ah
		mov     dx,bx                   ;
		add     dx,2                    ; CurTxt=CurTxt + 2
		lea     bx,_CurTxt              ;
		mov     [bx],dx                 ;

		;  (3) To Change CurCsr

		lea     bx,_CurCsr              ;
		mov     dx,[bx]                 ;
		mov     bx,dx

		mov     dh,20h                  ;
		mov     es:[bx],dh              ;
		mov     dh,02h                  ;
		mov     es:[bx+2],dh            ;

		mov     dx,bx                   ;
		add     dx,2                    ; CurCsr=CurCsr + 2
		lea     bx,_CurCsr              ;
		mov     [bx],dx                 ;

		mov     bx,offset _LocCsr
		mov     dl,[bx+1]
		inc     dl
		mov     [bx+1],dl
		@SetCsr _LocCsr

;                @popall
		mov     al,01h
		jmp     label2
label1:
;                @popall
		mov     al,00h
label2:

		pop     es
		pop     ds
		pop     dx
		pop     bx

		pop     bp

		ret
_ToCheck        endp



;=============================== 此為讀取新的text的函數,呼叫C語言 =====================
_Txt            proc

		mov     cx,60d
		lea     bx,_pic1
		add     bx,1130d
loop2:

		push    bx
		push    ds
		push    cx

		call    _rndlist          ; 此為C程式中的函數,傳回值放AH

		pop     cx
		pop     ds
		pop     bx

		mov     ds:[bx],al
		inc     bx

		loop    loop2             ; 一直放,直到放下60個為止

		ret

_Txt            endp


;==================================初始化整個的程式 ====================================
_initial        proc
;將ds指向我們的data segment
		mov     ax,@data
		mov     ds,ax
;求知到底現在是要求多少秒變一次
		call    _PerCh
		mov     cs:_PerChr,al

;放入新的Text
		call    _Txt

		;Initial Location

		mov     ax,2260
		mov     ds:_CurTxt,ax

		mov     ax,2100
		mov     ds:_CurBrg,ax

		mov     ax,1940
		mov     ds:_CurCsr,ax

		mov     ax,0c0ah
		mov     ds:_LocCsr,ah
		mov     ds:_LocCsr + 1,al

;將 pic1(ASCII) ,pic2(Property)的內容寫入記憶體
		mov     bx,offset _pic1
		mov     cx,07d0h
		mov     ax,0b800h
		mov     es,ax

		mov     di,00h
	loop1:
         ;放pic1的data
		mov     ax,[bx]
		mov     es:[di],ax

		inc     di
		;放pic2的data
		mov     ax,[bx + 07d0h]
		mov     es:[di],ax

		inc     di

		inc     bx
		loop    loop1

		@SetCsr _LocCsr            ;設定目前的遊標為人的開始處

		ret

_initial        endp
		end

3.Macro.h:(常用的巨集集合)
;========================設定遊標的巨集Set Cursor Macro=======================
; Register:
;   BH = Page Number
;   DH = The Row
;   DL = The Column
;   AH = 02h
; Parameter:
;   where = Current Cursor
@SetCsr         macro   where
		lea     bx,where
		mov     dh,[bx]
		mov     dl,[bx+1]
		mov     bh,00h
		mov     ah,02h
		int     10h
		endm

;===========================結束整個程式的巨集==========================
@EndRun         macro
		mov     ah,4ch
		int     21h
		endm

;=========================查得中斷向量表於 Ofs:BX,Seg:ES=======================
;AL = Interrupt Number
@GetINT         macro   intnum
		mov     ah,35h
		mov     al,intnum
		int     21h
		endm

;=========================更改中斷向量表於DS:DX ===============================
;AL = Interrupt Number
;Location Become = DS:DX
@ChgINT         macro   intnum
		mov     ah,25h
		mov     al,intnum
		int     21h
		endm

;========================抓取系統時間,以Packed BCD表示========================
@GetTime        macro
		mov     ah,02h
		int     1ah
		endm

;============================== Push All Register ==============================
@pushall        macro
		push    ax
		push    bx
		push    cx
		push    dx
		push    si
		push    di
		push    es
		push    ds
		pushf
		endm

;============================== Pop All Register ==============================

@popall         macro
		popf
		pop     ds
		pop     es
		pop     di
		pop     si
		pop     dx
		pop     cx
		pop     bx
		pop     ax
		endm

;=======================把 Packed BCD 給印出 在營幕右上角=======================
@ShwBCDNum      macro   Num,Ofs

		push    ax
		push    bx
		push    cx

		mov     ah,Num
		mov     al,Num

		and     ax,0f00fh

		mov     cl,04h
		shr     ah,cl

		add     ax,3030h      ;把數字給轉成為ASCII數字

		mov     bx,Ofs
		mov     es:[bx],ah
		add     bx,02h
		mov     es:[bx],al

		pop     cx
		pop     bx
		pop     ax
		endm

;==================顥示一個字元在螢幕上,此時,ES:Ofs ====================
@ShwChar        macro   char,Ofs
		push    ax
		push    bx
		mov     ah,char
		mov     bx,Ofs
		mov     es:[bx],ah
		pop     bx
		pop     ax

		endm

;==================利用Dos 中斷印出一個字元===============================
@PrintChar      macro   char
		mov     ah,02h
		mov     dl,char
		int     21h
		endm

;==================利用Dos 中斷印出一個字串直到$============================
@PrintStrn      macro   stn
		lea     dx,stn
		mov     ah,09h
		int     21h
		endm
5. MakeFile.bat (編譯的批次檔:但要有 ml.exe 及 bcc.exe)
ml /c /Cp /Cx /Zf prjasm.asm
bcc prjc.c prjasm.obj

五.缺失:
這個程式基本上的缺失,也就是還沒有達成的地方有下:
1. 不能功成而退:我的程式每在跑到跌入河中之後,也就是死亡之後,電腦也
就跟著當機了,基本上,我大概是覺得這樣的現象之所以會發生,主要還是
因為在我 CheckIfDie的副程式根本沒有很正當的結束,以致於造成原來的堆
壘大亂,所以造成當機,當然,亦有可能是因為INT 1CH沒有做好正當的恢
復所致,不過大體說起來,這個程式如果在您完完全全的打完之後,亦是可
以正當的結束
2. 時間的控制仍以整數為主:因為我的程式主要是以每幾秒變動一次作為主
體,所以,我的程式只有,"每分10個字(60/6)","每分12個字(60/5)","每分15
個字(60/4)","每分20個字(60/3)","每分30個字(60/2)","每分60個字(60/1)"等
六個情形,其實,這樣的情形實在是和實際的打字軟體不符.
3. 中文打字:這一次還有一個比較大的遺憾就是,沒有將“中文打字”給做出
來.
4. 讀檔問題:其實,讀檔的問題比較好解決,因此,如果我們仔細看一下我的
程式的話,我們便可以得知,其實,因為文字的來源是由C語言來做的,所
以其實要把原來的產生隨機文字改成讀檔即可,可是由於時間有限,所以在
上個星期一交出作業之時,還沒有將其改過去.
5. Level以及Class的顯現:我的程式並沒有將現在所處的Level以及Class的情
形給顯示出來,其實,這是不應該的.


←回列表  ↑上一篇  ↓下一篇        張貼  回應