写了 三个下午 + 三个晚上,总算搞定了:(竟然已经有人贴了,够快)
我可是现写的,调试太累了,800 多行代码:(
结构还算清晰,复杂的地方都有注释:
data segment
;
; consts
CR EQU 13
LF EQU 10
MaxRecCnt EQU 50
NameLen EQU 20
NumLen EQU 8
NameFieldLen EQU 1+1+NameLen
NumFieldLen EQU 1+1+NumLen
;
; string resources
strReturn db CR, LF, '$'
strInputName db 'Input a Name: $'
strInputNumber db 'Input a Telephone Number: $'
strDispName db 'Name:$'
intDispNameLen db $ - strDispName - 1
strDispNumber db 'Number:$'
strInsertAnother db 'Insert another record? (Y/N)$'
strWantNumber db 'Do you want a Telephone Number? (Y/N)$'
strNameNotFound db 'Do not have such a record.$'
;
; table of telephone notebook
Tele_Table db MaxRecCnt dup(NameLen,?,NameLen dup(0), NumLen,?,NumLen dup(0))
Tele_RecCount db 0
Tele_RecSize db NameFieldLen + NumFieldLen
;
; variables
NameBuffer db NameLen, ?, NameLen dup(0)
;
data ends
code segment
assume cs: code, ds: data
; procedure MinMax(Num1: cl; Num2: ch)
; move Min(Num1, Num2) to cl, and Max(Num1, Num2) to ch.
; (in): cx
; (out): cx
MinMax proc far
cmp cl, ch
jbe MinMax_Do_Not_Change
; else, cl > ch, do change
xchg cl, ch
MinMax_Do_Not_Change:
ret
MinMax endp
; procedure DispCRLF()
; display CRLF, to start a new line.
; (in): None
; (out): None
DispCRLF proc far
push ax
push dx
mov ah, 2
mov dl, CR
int 21h
mov dl, LF
int 21h
pop dx
pop ax
ret
DispCRLF endp
; procedure DispBlanks(Count: bx)
; display Count blanks.
; (in): bx
; (out): None
DispBlanks proc far
push dx
push bx
push ax
mov ah, 2
mov dl, ' '
Display_Blanks_Begin:
cmp bx, 0
jz Display_Blanks_Finish
int 21h
dec bx
jmp Display_Blanks_Begin
Display_Blanks_Finish:
pop ax
pop bx
pop dx
ret
DispBlanks endp
; procedure DispStr(Addr: dx)
; display the string, which address is spec. in Addr.
; (in): dx
; (out): None
DispStr proc far
push ax
mov ah, 9
int 21h
pop ax
ret
DispStr endp
; procedure DispStrCRLF(Addr: dx)
; display the string with CRLF, which address is spec. in Addr.
; (in): dx
; (out): None
DispStrCRLF proc far
push ax
mov ah, 9
int 21h
call DispCRLF
pop ax
ret
DispStrCRLF endp
; procedure InputChar(Buff: al)
; input a char.
; (in): None
; (out): al
InputChar proc far
push bx
mov bh, ah
mov ah, 1
int 21h
mov ah, bh
pop bx
ret
InputChar endp
; procedure InputStr(Addr: dx)
; input a string, [Addr] is the max size of buffer; [Addr + 1] is the actual
; size of the string; [Addr + 2] is the address of the inputed string.
; (in): dx
; (out): None
InputStr proc far
push ax
mov ah, 10
int 21h
pop ax
ret
InputStr endp
; procedure TerminateStr(Addr: dx)
; set the end of the string to '$' to STOP the display of this string.
; [Addr + 1] holds the actual size of the string; [Addr + 2] points to the
; beginning of the string.
; (in): dx
; (out): None
TerminateStr proc far
push dx
push bx
mov bx, dx
inc bx ; now, [bx] is the actual size of the string
mov bl, [bx] ; now, bl is the actual size of the string
xor bh, bh ; now, bx is the actual size of the string
add dx, 2 ; point to the beginning of the string
add dx, bx ; point to the end of the string
mov bx, dx
mov [bx], byte ptr '$' ; append a '$' to the end
pop bx
pop dx
ret
TerminateStr endp
; procedure CompareString(Src: si; Dst: di)
; compare 2 strings. [Src - 1] and [Dst - 1] hold the length of the string.
; Src < Dst: SF = 1, ZF = 0; Src > Dst: SF = 0, ZF = 0; Src = Dst: ZF = 1.
; (in): si, di
; (out): flags
CompareString proc far
push si
push di
push cx
push bx
push ax
mov cl, [si - 1] ; length of Src
mov ch, [di - 1] ; length of Dst
mov bx, cx
; if any string is nil
or cl, ch
jz Compare_Equal ; length of both strings are zero
cmp bl, 0
je Compare_Below ; length of Src is zero
cmp bh, 0
je Compare_Above ; length of Dst is zero
mov cx, bx ; restore cx
; make cx = Min(cl, ch), it will be the counter below
call MinMax ; now, cl = Min(ch, cl)
xor ch, ch ; now, cx = Min(ch, cl)
; start compare
Compare_Next_Char:
mov al, [si]
mov ah, [di]
cmp al, ah
jb Compare_Below
ja Compare_Above
inc si
inc di
dec cx
jnz Compare_Next_Char
; comes to here, only 2 conditions: SubStr or Equal
cmp bl, bh ; just check the length of them
jb Compare_Below
ja Compare_Above
Compare_Equal:
xor bx, bx ; now, ZF = 1
jmp Compare_Finish
Compare_Below:
add bx, 0 ; now, ZF = 0
stc ; set CF to 1
jmp Compare_Finish
Compare_Above:
add bx, 0 ; now, ZF = 0
clc ; set CF to 0
Compare_Finish:
pop ax
pop bx
pop cx
pop di
pop si
ret
CompareString endp
; procedure XChangeMemory(Src: si; Dst: di; Len: cx)
; swap 2 block of memory. size is spec. in cx.
; Src <-> Dst
; (in): si, di, cx
; (out): None
XChangeMemory proc far
push di
push si
push cx
push bx
XChange_Begin:
jcxz XChange_Finish
mov bl, [si]
mov bh, [di]
mov [si], bh
mov [di], bl
inc si
inc di
dec cx
jmp XChange_Begin
XChange_Finish:
pop bx
pop cx
pop si
pop di
ret
XChangeMemory endp
; procedure CalcRecOffset(RecNo: cx)
; calculate the offset of a spec. record.
; the result is add to dx
; (in): cx
; (out): dx
CalcRecOffset proc far
push cx
; loop to calculate the offset of a spec. record
Start_Calculate:
test cx, cx
jz Finish_Calculate
add dl, Tele_RecSize
adc dh, 0
dec cx
jmp Start_Calculate
Finish_Calculate:
pop cx
ret
CalcRecOffset endp
; procedure QueryInputName()
; display the query string before input name.
; (in): None
; (out): None
QueryInputName proc far
push dx
lea dx, strInputName
call DispStr
pop dx
ret
QueryInputName endp
; procedure InputName(Buffer: dx)
; input a name into Buffer.
; (in): dx
; (out): None
InputName proc far
call InputStr
call TerminateStr
ret
InputName endp
; procedure DisplayName(RecNo: cx)
; display the name in the record which record no. is spec. in RecNo.
; (in): cx
; (out): None
DisplayName proc far
push dx
push bx
lea dx, Tele_Table
call CalcRecOffset
; now, dx holds the address of the spec. record
add dx, 2 ; the string buffer is at offset + 2
call DispStr
; display some blanks to fit to the same width with other names
mov bx, dx
dec bx ; now, [bx] is the actual length of string
mov bl, [bx]
xor bh, bh ; clear bh
mov dx, bx ; dx is the actual length of string
mov bx, NameLen
sub bx, dx ; bx is the number of blanks to be displayed
call DispBlanks
pop bx
pop dx
ret
DisplayName endp
; procedure QueryInputNumber()
; display the query string before input name.
; (in): None
; (out): None
QueryInputNumber proc far
push dx
lea dx, strInputNumber
call DispStr
pop dx
ret
QueryInputNumber endp
; procedure InputNumber(Buffer: dx)
; input a telephone number into Buffer
; (in): dx
; (out): None
InputNumber proc far
push dx
add dx, NameFieldLen ; point to the address of Num. field
call InputStr
call TerminateStr
pop dx
ret
InputNumber endp
; procedure DisplayNumber(RecNo: cx)
; display the telephone num in the record which record no. is spec. in RecNo.
; (in): cx
; (out): None
DisplayNumber proc far
push dx
lea dx, Tele_Table
call CalcRecOffset
; now, dx holds the address of the spec. record
add dx, NameFieldLen ; point to the address of Num. field
add dx, 2 ; the string buffer is at offset + 2
call DispStr
pop dx
ret
DisplayNumber endp
; procedure AppendRecord()
; append a record behind the telephone notebook.
; (in): None
; (out): None
AppendRecord proc far
push dx
push cx
lea dx, Tele_Table
xor cx, cx
mov cl, Tele_RecCount
call CalcRecOffset
; now, dx holds the address of the new record to be inserted
call QueryInputName
call InputName
call DispCRLF
call QueryInputNumber
call InputNumber
call DispCRLF
inc Tele_RecCount
pop cx
pop dx
ret
AppendRecord endp
; procedure DisplayRecord(RecNo: cx)
; display a spec. record which record no. is spec. in RecNo.
; (in): cx
; (out): None
DisplayRecord proc far
call DisplayName
call DisplayNumber
call DispCRLF
ret
DisplayRecord endp
; procedure AppendRecords()
; append records in a loop
; (in): None
; (out): None
AppendRecords proc far
push dx
push ax
Continue_Append:
call DispCRLF
call AppendRecord
call DispCRLF
lea dx, strInsertAnother
call DispStr
call InputChar ; result is in al
cmp al, 'y'
jz Continue_Append
cmp al, 'Y'
jz Continue_Append
call DispCRLF
pop ax
pop dx
ret
AppendRecords endp
; procedure DisplayRecTitle()
; display the column title of records.
; (in): None
; (out): None
DisplayRecTitle proc far
push dx
push bx
; display the name title
lea dx, strDispName
call DispStr
xor bh, bh
mov bl, NameLen
sub bl, intDispNameLen
call DispBlanks
; display the number title
lea dx, strDispNumber
call DispStrCRLF
pop bx
pop dx
ret
DisplayRecTitle endp
; procedure DisplayRecords()
; display all the records in the telephone notebook.
; (in): None
; (out): None
DisplayRecords proc far
push dx
push cx
push bx
call DispCRLF
call DisplayRecTitle
; display contents
xor bx, bx
mov bl, Tele_RecCount
xor cx, cx
Display_Next_Record:
call DisplayRecord
inc cx
cmp bx, cx
jg Display_Next_Record
pop bx
pop cx
pop dx
ret
DisplayRecords endp
; procedure CompareRecName(RecA: bl; RecB: bh)
; compare 2 names, NameA and NameB is the record no.
; NameA < NameB: CF = 1, ZF = 0; NameA > NameB: CF = 0, ZF = 0;
; NameA = NameB: ZF = 1.
; (in): bx
; (out): flags
CompareRecName proc far
push si
push di
push dx
push cx
cmp bl, bh ; if RecA = RecB, do not compare
je Compare_Rec_Same
; si <- NameA
lea dx, Tele_Table
xor cx, cx
mov cl, bl
call CalcRecOffset
mov si, dx ; point to the record
inc si
inc si ; point to the Name buffer
; di <- NameB
lea dx, Tele_Table
xor cx, cx
mov cl, bh
call CalcRecOffset
mov di, dx ; point to the record
inc di
inc di ; point to the Name buffer
call CompareString
; flags have been set already
jmp Compare_Rec_Finish
Compare_Rec_Same:
xor cx, cx ; set ZF = 1
Compare_Rec_Finish:
pop cx
pop dx
pop di
pop si
ret
CompareRecName endp
; procedure XChangeRecord(RecA: bl; RecB: bh)
; swap 2 records, RecA and RecB is the record no.
; RecA <-> RecB
; (in): bx
; (out): None
XChangeRecord proc far
push si
push di
push dx
push cx
cmp bl, bh ; if RecA = RecB, do not change
je XChange_Rec_Finish
; si <- RecA
lea dx, Tele_Table
xor cx, cx
mov cl, bl
call CalcRecOffset
mov si, dx ; point to the record
; di <- RecB
lea dx, Tele_Table
xor cx, cx
mov cl, bh
call CalcRecOffset
mov di, dx ; point to the record
xor ch, ch
mov cl, Tele_RecSize
call XChangeMemory
XChange_Rec_Finish:
pop cx
pop dx
pop di
pop si
ret
XChangeRecord endp
; procedure SortRecordsByName()
; sort all the records by name
; (in): None
; (out): None
SortRecordsByName proc far
push dx
push cx
push bx
push ax
xor ch, ch
mov cl, Tele_RecCount
cmp cl, 1
jle Sort_Name_Finish; if record count <= 1, do not sort
dec cx ; record no. is starts from index 0
;for dl := 0 to cl - 1 do
;begin
; al := dl;
; for dh := dl + 1 to cl do
; begin
; bl := al;
; bh := dh;
; CompareRecName;
; if Rec(al) > Rec(dh) then
; al := dh;
; end;
; bl := dl;
; bh := al;
; XChangeRecord;
;end;
xor dx, dx
Sort_Name_Outer_Loop:
mov al, dl ; use al to store the Min name rec. no.
mov dh, dl
inc dh ; dh = dl + 1
Sort_Name_Inner_Loop:
mov bl, al
mov bh, dh
call CompareRecName ; Rec(Min) - Rec(dh)
jbe Sort_Name_Below_Or_Equal
; Rec(min) > Rec(dh), so dh is the new Min
mov al, dh ; move new Min to al
Sort_Name_Below_Or_Equal:
inc dh
cmp dh, cl
jbe Sort_Name_Inner_Loop
mov bl, dl
mov bh, al
call XChangeRecord ; exchange Rec(dl) and Rec(Min)
inc dl
cmp dl, cl
jb Sort_Name_Outer_Loop
Sort_Name_Finish:
pop ax
pop bx
pop cx
pop dx
ret
SortRecordsByName endp
; procedure FindRecordByName(Name: dx)
; find a certain record by Name. result record no. is in cl.
; if Name not found, cl is 0FFH. ch is NOT certain!
; (in): dx
; (out): cx
FindRecordByName proc far
push di
push si
push dx
push bx
xor cl, cl
mov ch, Tele_RecCount
cmp ch, 0 ; no record to be found
jz Find_Record_Name_Not_Found
xor bh, bh
mov bl, Tele_RecSize
mov si, dx ; point si to the name user input
lea di, Tele_Table ; point di to names in the notebook
add si, 2 ; point to the string buffer
add di, 2 ; point to the string buffer
Find_Next_Record_Name:
call CompareString
jz Find_Record_Name_Finish ; with flag set already
add di, bx ; point di to the string buffer of next record
inc cl
cmp ch, cl
ja Find_Next_Record_Name
; comes here, means not found
Find_Record_Name_Not_Found:
mov cl, 0FFH
Find_Record_Name_Finish:
pop bx
pop dx
pop si
pop di
ret
FindRecordByName endp
; procedure DispFindResult(RecNo: cl)
; display the result of find.
; (in): cl
; (out): None
DispFindResult proc far
push dx
push cx
cmp cl, 0FFH
jne Disp_Record_Name_Found
Disp_Record_Name_Not_Found:
call DispCRLF
lea dx, strNameNotFound
call DispStrCRLF
jmp Disp_Record_Result_Finish
Disp_Record_Name_Found:
call DispCRLF
xor ch, ch
call DisplayRecTitle
call DisplayRecord
Disp_Record_Result_Finish:
pop cx
pop dx
ret
DispFindResult endp
; procedure FindRecord()
; find a certain record which the name is spec. by user.
; (in): None
; (out): None
FindRecord proc far
push dx
push ax
Continue_Find_Record:
call DispCRLF
lea dx, strWantNumber
call DispStr
call InputChar ; result is in al
xor al, 'Y'
and al, 0DFH ; if input 'y' or 'Y', al will be 0
jnz Find_Record_Finish
; else, do find
call DispCRLF
call QueryInputName
lea dx, NameBuffer
call InputName
call FindRecordByName
call DispFindResult
jmp Continue_Find_Record
Find_Record_Finish:
pop ax
pop dx
ret
FindRecord endp
; procedure main
main proc far
start:
push ds
xor ax, ax
push ax
mov ax, data
mov ds, ax
mov es, ax
; main code starts here
call AppendRecords
call DisplayRecords
call SortRecordsByName
call DisplayRecords
call FindRecord
ret
main endp
code ends
end start