插入排序,只对哈希表中的序号进行排序,学生信息直接按顺序存入内存,输出排序结果时,按哈希表中的序号进行输出
;begin: 2009年 12月 01日 星期二 13:10:06 CST
;end:2009年 12月 03日 星期四 22:34:37 CST
;author: guozan-scst-bupt
;description:students' score record, include student's number/name/score
;comment:不应该想那么多奇怪的方法,反而在汇编里面变的很麻烦,代码长度急剧上升
MAX_NUM equ 10
;打印换行
nextline macro
push dx
push ax
mov dl, 0ah
mov ah, 2
int 21h
pop ax
pop dx
endm
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
dseg segment
num db MAX_NUM dup (30, 0, 30 dup (0))
names db MAX_NUM dup (30, 0, 30 dup (0))
sco_int db MAX_NUM dup (0)
sco_dec db MAX_NUM dup (0)
sum_int dw 0
sum_dec dw 0
hash db MAX_NUM dup (0)
welcome db 'Welcome to this program! Created by guozan!$' ;进入程序的提示信息
ask_total db 'Please enter the total number of students:$' ;学生总人数
ask_number db 'Please enter the number:$' ;询问学号
ask_name db 'Please enter the name, support english only:$' ;询问姓名,英语的名字
ask_score db 'Please enter the score, decimal is one:$' ;询问成绩,精确到小数点后一位
exit_info db 'Succeed exit!$'
dseg ends
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;*****************************************************************
cseg segment
assume cs:cseg, ds:dseg
start:
mov ax, dseg
mov ds, ax
;输出欢迎信息
lea dx, welcome
mov ah, 09h
int 21h
nextline
;ask_total
lea dx, ask_total
mov ah, 09h
int 21h
;get total
mov ah, 1
int 21h
sub al, '0'
mov cl, al ;将数目放入cl
xor ch, ch ;cx决定读入的数据放入哪个位置
nextline
push cx ;保存总共的个数,方便打印,print和排序时使用
get_input:
or cx, cx
jz print
mov ax, cx ;bx = (cx-1) * 32;用于以后往内存存数据用;倒着存的
dec ax
mov bx, 32 ;30 + 1 + 1
mul bx
mov bx, ax ;bx 中是偏移量
;读入三个信息
;********************************************************
;please enter number
lea dx, ask_number
mov ah, 09h
int 21h
;get the num
lea dx, num
add dx, bx ;
mov ah, 0ah
int 21h
nextline
push bx ;103
mov bx, dx ;这次读如的地址,方便下面在尾部加上$
xor ah, ah
mov al, [bx + 1] ;把读入的字符数目放入Ax
add dx, 2 ;移动到字符数目位置后面一位
add dx, ax ;移动到最后一个字符后面
mov al, '$' ;结尾符
mov bx, dx
mov [bx], al ;植入结尾
;---------------------------------------
;pleas enter name
lea dx, ask_name
mov ah, 09h
int 21h
;get the name
pop bx ;87
lea dx, names
add dx, bx ;bx中是偏移量,来自enternumber
mov ah, 0ah
int 21h
nextline
mov bx, dx
xor ah, ah
mov al, [bx + 1] ;把读入的字符数目放入Ax
add dx, 2 ;移动到字符数目位置后面一位
add dx, ax ;移动到最后一个字符后面
mov al, '$' ;结尾符
mov bx, dx
mov [bx], al ;植入结尾
;---------------------------------------
;please enter score
lea dx, ask_score
mov ah, 09h
int 21h
;get the score
mov dx, 0 ;dx 里存的是和,所以先清0
get_int:
mov ah, 1
int 21h
cmp al, '.'
jz get_deci
sub al, '0'
xor ah, ah
push ax
;读入了一个数,然后dx*10 + al,结果存储在dx中,存的就是一个数
mov ax, dx ;ax = dx
mov bl, 10
mul bl ;ax = ax * 10
pop dx ;dx = old_ax, 读入的那个al
add dx, ax ;dx = old_ax + ax(dx) * 10
jmp get_int
get_deci:
mov ax, dx ;把计算出的结果转移一下
lea dx, sco_int ;dx = sco_int + cx - 1
mov bx, cx
dec bx ;存储的偏移量
add bx, dx ;这次的整数放的位置
mov [bx],al ;把整数部分存入内存
xor ah, ah
push ax ;把整数部分放入堆栈,保存一下,方便下面的排序
;add to sum_int
lea bx, sum_int
mov dx, [bx]
add ax, dx ;sum_int + 这次的读入的整数
mov [bx], ax ;结果存入内存
;读入小数部分,一位
mov ah, 1
int 21h
sub al, '0'
xor ah, ah
lea dx, sco_dec
mov bx, cx
dec bx ;计算出偏移量
add bx, dx ;算出此次存储地址;bx可以用整数时用的,不用改
xor ah, ah
mov [bx], al ;小数部分存入内存
push ax ;小数部分压入堆栈
;add deci to sum_dec
lea bx, sum_dec
mov dx, [bx] ;读出来数据
add ax, dx ;加上此次读入的小数部分
mov [bx], ax ;放回内存
mov ah, 1
int 21h ;读一个回车,提高用户体验
;nextline
;--------------------------------------------------------
;使用hash表去标记,hash中的存的只是排序后此项在num,name数组中的位置
insert:
pop ax ;小数部分
pop dx ;整数部分
pop bx ;取出输入的数据总数,从初始第一次push 的cx中读出来的
push bx ;重新放回
call insert_table
;lea bx, hash ;为修改hash中的数据做准备,得到首地址
;mov ax, cx ;[bx + cx - 1] = cx - 1
;dec ax
;add bx, ax
;mov [bx], al
;test ok, get the total number of input, and then give it back
;pop dx
;push dx
;nextline
;add dx, '0'
;mov ah, 2
;int 21h
;nextline
;call insert_table
;-----------------------------------------------------
dec cx ;循环控制
jmp get_input ;进入下一个读入循环
;***************************************************
print:
;打印平均
lea bx, sum_int ;整数部分的和
mov ax, [bx]
pop cx ;总共的数目,来自于第一个push,刚读入总数的时候
push cx ;保存cx
div cl ;除法,求平均
push ax ;保存除的结果
mov al, ah ;把余数转移
xor ah, ah
xor bh, bh
mov bl, 10
mul bl ;ax = al * 10
lea bx, sum_dec ;小数部分的和
mov dx, [bx]
add ax, dx ;整数的余数 * 10 + 小数部分,得到新的小数部分
div cl
xor ah, ah ;小数部分求平均,只保存商,余数丢弃
xor bh, bh
mov bl, 10
div bl ;ax / 10,商可能太大,需要向高位进位
mov bl, al ;商转移到bx
mov al, ah ;余数放到ax,也就是小数位
xor ah, ah
pop dx ;把整数除的结果弹出来
add dx, bx ;把小数部分的进位加上
push ax ;存小数
mov ax, dx ;打印整数部分
xor ah, ah ;把余数清掉
call print_num
mov dl, '.'
mov ah, 2
int 21h ;打印小数点
pop ax ;打印小数
call print_num
nextline
lea bx, hash ;哈希表的首地址
pop cx ;
print_loop:
push bx ;地址保存,因为循环里面用到了bx
xor dh, dh
mov dl, [bx] ;dx标记此次要输出的数据的地址,这时还只是标号
push dx ;把序号保存,打印成绩的时候用
;sub dx, '0'
;dx = dx * 32 + num[hash[i]]
;计算出偏移量,放在ax中,打印学号和名字时使用
mov ax, 32
mul dx
push ax ;保存偏移量,name num的偏移量都是ax
;输出学号
lea dx, num
add ax, 2 ;+ 2,前方有总数和读入数目
add dx, ax ;偏移地址
mov ah, 09h
int 21h
mov dl, ' ' ;空格
mov ah, 2
int 21h
;输出姓名
pop ax ;偏移量
lea dx, names
add ax, 2
add dx, ax
mov ah, 09h
int 21h
mov dl, ' ';空格
mov ah, 2
int 21h
;输出成绩
;整数部分
lea bx, sco_int
pop ax ;序号
push ax ;方便小数部分使用
add bx, ax ;偏移后的位置,成绩所在位置
mov al, [bx] ;把当前位置的成绩当作参数,call print_num,输出道屏幕
call print_num
;小数点
mov dl, '.'
mov ah, 2
int 21h
;小数部分
lea bx, sco_dec
pop ax
add bx, ax
mov al, [bx]
call print_num
nextline
pop bx
inc bx
loop print_loop
exit:
lea dx, exit_info
mov ah, 09h
int 21h
mov ah, 4ch
int 21h
;要打印的数放到ax中,成绩不可能大于100
print_num proc near
push ax
push bx
push cx
push dx
mov cx, 0 ;特殊用途,标记百位是不是1,默认是0
hundred: ;百位
mov bl, 100
div bl
push ax ;保存相除的结果
or al, al ;看商是否是0
jz deca ;百位是0,不用打印,处理余数,剩下的十位和个位
mov dl, al ;打印字符,商
add dl, '0'
mov ah, 2
int 21h
mov cx, 1 ;标记有百位
deca: ;十位
pop ax ;读出相除的结果
mov al, ah ;把余数当作被除数
xor ah, ah
mov bl, 10
div bl
push ax ;保存相除的结果
or cx, cx ;检查百位是否是0,是0,可以不打印十位的0,是1,必须打印0
jnz print_deca ;无条件打印十位
or al, al
jz over ;十位是0,不用打印
print_deca: ;无条件打印十位
mov dl, al ;打印商
add dl, '0'
mov ah, 2
int 21h
over: ;个位,无条件必须打印
pop ax ;读出相除的结果
mov dl, ah ;把余数放到 dl 中打印
add dl, '0'
mov ah, 2
int 21h
pop dx
pop cx
pop bx
pop ax
ret
print_num endp
;分析数据,然后放入数据hash表
insert_table proc ;序列号就是偏移量,放入table中的是序列号
push ax ;小数部分
push bx ;总共输入的数量
push cx ;当前输入的第几个,倒着的5,4,3,2,1
push dx ;整数部分
;找到要比较的原串
lea di, hash
add di, bx ;当前整数部分数组末尾处的偏移量
dec di ;末尾处
cmp_int:
cmp bx, cx ;比较到了哪一个,如果到了自己,也就是bx-到正好是要输入的那个,放入
je put_in
lea si, sco_int ;整数部分的地址
push ax
mov ax, [di] ;di中一次取出来两个字节,需要转换一下
xor ah, ah
add si, ax
pop ax
;add si, [di];取出了两个字节 ;取出当前hash位置存放的序号,然后加上si,得到这次的整数的位置
cmp dl, [si] ;整数部分和当前位置的整数部分比较,貌似这样就会只取出来一个字符
jl mov_space ;新来的比较小,放入这个位置,把这个位置移出来
je cmp_dec ;整数相同,比较小数部分
dec bx ;正在比较的是bx
dec di
jmp cmp_int
cmp_dec:
push ax
lea si, sco_dec
mov ax, [di]
xor ah, ah
add si, ax ;小数存储的物理地址
pop ax
cmp al, [si]
jle mov_space ;相等或比较小,放入这个位置
dec bx ;正在比较的是bx
dec di
jmp cmp_int ;比较大,接着比较
mov_space:
lea si, hash ;从hash的头部开始移动
mov dx, 1
loop1:
mov al, [si+1]
mov [si], al ;[si] = [si + 1]
inc si
dec bx
cmp bx, dx ;如果等于1,就是说总共移动了bx - 1次
je put_in
jmp loop1
put_in:
dec cx
mov [di], cl
pop dx
pop cx
pop bx
pop ax
ret
insert_table endp
cseg ends
end start