Proteus电梯课设

通过Proteus实现8层楼电梯:
8层楼的电梯,
可以实现内部和外部的按键的功能,
以及通过的算法实现楼层的选择。
有数码管显示和lcd显示,
以及到达楼层后的铃声提示。

具体实现见代码注释:

DATAS SEGMENT
    ;此处输入数据段代码
    IOY0 equ 0E000h
    IOY1 equ 0E040h
    IOY2 equ 0E080h
    IOY3 equ 0E0C0h
    IOY4 equ 0E100h
    First_A equ IOY0+00H*4
    First_B equ IOY0+01H*4
    First_C equ IOY0+02H*4
    First_MODE equ IOY0+03H*4
    Seq db 20 dup(?);记录按下的三个序号
    Ordered db 20 dup(?);记录下排序之后的seq数组
    ordered_num db 00h
    num db 0
    LED db 01h,02h,04h,08h,10h,20h,40h,80h;点亮对应的部分
    Out_LED db 0feh,0fdh,0fbh,0f7h,0efh,0dfh,0bfh,7fh;熄灭对应的部分
    Current_LED db ?
    Row_disp db 0feh,0fdh,0fbh,0f7h
    Stop_flag db 00h;停止标志
    Stop_mode db 00h;存储当前的停止运行标志
    ;-------------------------------------------
    Second_A equ IOY1+00h*2
    Second_B equ IOY1+01h*2
    Second_C equ IOY1+02h*2
    Second_MODE equ IOY1+03h*2
    buf1 db 'Welcome'
    length1 db $-buf1
    buf2 db 'Up'
    length2 db $-buf2
    buf3 db 'Down'
    length3 db $-buf3
    buf4 db 'Close'
    length4 db $-buf4
    buf5 db 'Open'
    length5 db $-buf5
    buf6 db 'Run'
    length6 db $-buf6
    buf7 db 4 dup(' ')
    length7 db $-buf7
    Row db ?
    Line db ?
	;----------------------------------
    MY8253_COUNT0 equ IOY2+00H*4
    MY8253_COUNT1 equ IOY2+01H*4
    MY8253_COUNT2 equ IOY2+02H*4
    MY8253_MODE equ IOY2+03H*4
    ;----------------------------------
    current_floor db 01h;当前楼层
    aim_floor db ?;输入的目标楼层
    ;----------------------------------
    Third_A equ IOY3+00h*2
    Third_B equ IOY3+01h*2
    Third_C equ IOY3+02h*2
    Third_Mode equ IOY3+03h*3
    ;----------------------------------
    TFREQ dw 264,264,396,396,0
    TDLY dw 6000,6000,6000,6000
    ;----------------------------------
    Fourth_A equ IOY4+00h*4
    Fourth_B equ IOY4+01h*4
    Fourth_C equ IOY4+02h*4
    Fourth_Mode equ IOY4+03h*4
    Outside db 20 dup(?);外部输入楼层
    Out_num db 00h
    Current_up_led db 00h
    Current_down_led db 00h
    Current_dir db 01h;00h表示下行,01h表示上行
    ;----------------------------------
    max db ?;放入当前数据的最大值
    dir db ?;表示运行的方向,1是上行,0是下行
    sort_dir db ?;表示冒泡排序的方向,0表示正向排序,1表示反向排序
	tmp db 20 dup(?);用于暂存的数据
    tmp_num db 00h
DATAS ENDS

STACKS SEGMENT
    ;此处输入堆栈段代码
    dw 50 dup(?)
    top label byte
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
	mov ax,stacks
	mov ss,ax
	mov sp,offset top
    MOV AX,DATAS
    MOV DS,AX
    ;此处输入代码段代码
    ;call Lcd_main
    ;call Running
    ;初始化当前楼层显示1
    mov dx,Third_mode
	mov al,10000010B;A口输出,B口输入,C口第四位输出
	out dx,al;设置当前8255的模式
	mov al,current_floor
	mov dx,Third_A
	out dx,al
	;------------------------------------
	;设置外部键盘8255的模式
	mov dx,Fourth_Mode 
    mov al,10001000B;A,B都为输出信号,C的高位为输入信号,D的高位为输出信号
    out dx,al
	call Lcd_main
Again:
    call keyboard
    jmp again
;-------------------------------------
Keyboard proc near
	mov al,10001000B;A为输出信号,C的高位为输入信号列,低位为输出信号行
	mov dx,First_mode
	out dx,al
	mov bh,00h;从第零行开始扫描
	mov bl,00h
Restart:
	call disp
Detect_row:
	mov dx,First_C
	mov al,row_disp[bx]
	out dx,al
	in al,dx
	and al,0f0h
	cmp al,0f0h
	jnc next_line;无输入直接转到下一行
	mov cx,4000h
	loop $
	in al,dx
	and al,0f0h
	cmp al,0f0h
	jnc next_line
	mov dl,0;用于记录该行中列的数据
	mov cx,4
Find_line:
	shl al,1
	jnc record_line;无进位说明是列
	inc dl
	loop Find_line
Record_line:
	mov al,bl;行的数据保存在bl中,列的数据保存在dl中
	shl al,1
	shl al,1
	mov dh,03h
	sub dh,dl
	add al,dh
	call xiaodou
	;记录之后继续扫描下一行
Next_line:
	call delay
	add bl,01h
	;test bl,04h
	;jz Detect_row;没有扫描完继续扫描下一行
	and bl,0fbh
	jmp restart
Stop:
	ret
Keyboard endp
	
Xiaodou proc near;判断两次输入的seq是否相同,相同只当一次
	push dx
	push bx
	mov bh,00h
	mov bl,num;bl表示序号,al表示输入的键值
	cmp al,07h;输入键的范围超过数字键的范围
	ja function;输入的可能是功能键也可能是无效键
	cmp bl,01h;至少当al为01的时候才有比较的意义
	jae compare
	jmp save;num为00h进入保存阶段
;----------------------------------------------
compare:;需要看这次输入的数据以前是否输入过
	;dec bl;上一次的数据
	;mov dl,seq[bx];上一次的数据存储在dl中,seq的末尾
	;cmp dl,al;这一次的数据存储在al中
	;je stop1;如果相等退出,不记录这一次的结果
	;cmp bl,01h;如果bl大于等于02h说明超范围了
	;ja lights_out 
	;inc bl
	mov cl,al
	mov ch,00h
	mov ah,01h
	cmp cx,00h
	ja loop_com;如果是0的话不能进入左移
	jmp end_com
Loop_com:
	shl ah,01h
	loop loop_com
End_com:
	mov dl,current_led
	test dl,ah
	jnz stop1
	cmp bl,02h
	ja lights_out
;----------------------------------------------
save:
	mov seq[bx],al;将数据记录到seq中去
	inc num;num的数值自增1,当num的数值大于3时,自动清零
	jmp stop1
lights_out:
	mov num,00h
	mov al,00h;这里不应该只对应显示功能在后面的停止电梯中页应该用到
	mov dx,First_A 
	out dx,al
	jmp stop1
Function:
	cmp al,0bh;超出范围表示无效
	ja stop1
	cmp al,08h;启动键
	je Run_ele
	cmp al,09h;紧急停止键
	je Stop_ele
	cmp al,0ah;求助键
	je Help_ele
	cmp al,0bh;进入外部按键的选择
	je Out_ele
Run_ele:;启动将数据从seq中装入aim_floor,显示close
	;应该要分成三种情况
	;第一种当前楼层在输入的楼层之间,默认先上行
	;第二种情况是当前楼层比输入的楼层低
	;第三种情况是当前楼层比输入的楼层高
	;第四种情况无输入楼层
	;输入完成之后要将已经到达的楼层从seq中清除出去
	call process;ordered存储的是排好序的数据
	cmp ordered_num,00h;首先要排除没有输入的情况
	je stop1
	;-------------------------------------------
	;运行部分
	mov cl,ordered_num;循环次数为元素的个数
	mov ch,00h
	lea bx,ordered
	;-------------------------------------------
	;测试处理的结果
	;mov bx,01h
	;mov al,ordered[bx]
	;call show_number
	;jmp $
	;经过测试ordered中的数据正确
	;-------------------------------------------
Loop_running:;意思是seq中的每一层都去
	mov al,[bx];seq中的最后一个数据应该先去
	inc al;真实层数应该加1
	mov aim_floor,al
	call Running;运行程序
	;-------------------------------------------
	mov row,01h
   	mov line,04h
   	lea di,buf4;显示'Close',在关门的时候显示
   	push cx
   	mov cl,length4
   	call show_string
   	pop cx
   	call Wait_5s
	;-------------------------------------------
	;清除部分,熄灭对应数码管,从seq中清除,num自减1
	mov dh,00h
	mov dl,[bx]
	mov si,dx
	;---------------------------------------------
	;熄灭主程序部分程序
	mov ah,current_led;将当前的led显示放入ah中
	mov al,Out_led[si];将熄灭数据放入al中
	and al,ah;熄灭后的新输出
	mov dx,First_A
	out dx,al
	mov current_led,al;输入新的输出数据
	;---------------------------------------------
	;在程序里剥离了方向关系,可是这里其实是有方向关系的
	;有方向和楼层
	;---------------------------------------------
	;mov al,current_dir
	;cmp al,00h;00h为下行,01h为上行,还没有解决的问题是同时有上有下即重复的情况,转场的情况
	;je Down_out
	;jmp up_out
	;-------------------------------------------------
	cmp cx,01h;只剩下最后一个楼层的情况
	je loop_end
	mov dl,[bx]
	mov dh,[bx+1]
	cmp dl,dh
	jbe up_pro;这一个时刻比下一个时刻小是上行
	jmp down_pro
Up_pro:
	mov al,01h;01h为上行
	mov ah,current_dir
	xor al,ah;如果异或的结果相同说明是上行
	cmp al,00h
	je up_out
	jmp both_out
Down_pro:
	mov al,00h;00h为下行
	mov ah,current_dir
	xor al,ah;如果异或的结果相同说明为下行
	cmp al,00h
	je down_out
	jmp both_out
	;-------------------------------------------------
Down_out:
	mov ah,current_down_led
	mov al,out_led[si]
	and al,ah
	mov dx,Fourth_B
	out dx,al
	mov current_down_led,al
	jmp out_end
Up_out:
	mov ah,current_up_led
	mov al,out_led[si]
	and al,ah
	mov dx,Fourth_A
	out dx,al
	mov current_up_led,al
	jmp out_end
;------------------------------------------
Both_out:
	mov ah,current_down_led
	mov al,out_led[si]
	and al,ah
	mov dx,Fourth_B
	out dx,al
	mov current_down_led,al
	;--------------------------
	mov ah,current_up_led
	mov al,out_led[si]
	and al,ah
	mov dx,Fourth_A
	out dx,al
	mov current_up_led,al
;------------------------------------------
Out_end:
	inc bx;bx自增1
	;--------------------------------------
	;模拟cx语句
	dec cx
	cmp cx,00h
	jne loop_running
	;--------------------------------------
	;loop Loop_running
loop_end:
	mov num,00h
	mov ordered_num,00h
	mov out_num,00h
	;熄灭外部中的所有灯
	mov al,00h
	mov dx,Fourth_A
	out dx,al
	mov dx,Fourth_B
	out dx,al
	mov current_up_led,al
	mov current_down_led,al
	jmp stop1
Stop_ele:;停止中断running
	call stop_running
	jmp stop1
Help_ele:;求助,这个用在串口通信中
	jmp stop1
Out_ele:
	call keyboard2
stop1:
	pop bx
	pop dx
	ret
Xiaodou endp
;-----------------------------------------
;键盘2的相关函数
Keyboard2 proc near
	mov dx,Fourth_A
    mov al,00h
    out dx,al
    mov dx,Fourth_B
    mov al,00h
    out dx,al
    mov bx,00h;从第零行开始扫描
Restart:
Detect_row:
	mov dx,Fourth_C
	mov al,row_disp[bx]
	out dx,al
	in al,dx
	and al,0f0h
	cmp al,0f0h
	jnc next_line;无输入直接转到下一行
	mov cx,5000h
	loop $
	in al,dx
	and al,0f0h
	cmp al,0f0h
	jnc next_line
	mov dl,0;用于记录该行中列的数据
	mov cx,4
Find_line:
	shl al,1
	jnc record_line;无进位说明是列
	inc dl
	loop Find_line
Record_line:
	mov al,bl;行的数据保存在bl中,列的数据保存在dl中
	shl al,1
	shl al,1
	mov dh,03h
	sub dh,dl
	add al,dh
	cmp al,07h;如果按下了功能键返回主键盘
	je return_key
	call Xiaodou2;al中的值没有改变
	call disp2;使用al中的值
	;记录之后继续扫描下一行
Next_line:
	call delay
	add bl,01h
	and bl,0fbh
	jmp restart
	ret
Return_key:;返回主键盘部分
	ret
Keyboard2 endp
	
Xiaodou2 proc near;判断两次输入的seq是否相同,相同只当一次
	push dx
	push bx
	mov bh,00h
	mov bl,out_num
	cmp bl,01h;至少当al为01的时候才有比较的意义
	jae compare
	cmp bl,0fh
	ja lights_out
	jmp save;num为00h进入保存阶段
;-------------------------------------------
compare:
	;dec bl;上一次的数据
	;mov dl,Outside[bx];上一次的数据存储在dl中,seq的末尾
	;cmp dl,al;这一次的数据存储在al中
	;je stop1;如果相等退出,不记录这一次的结果 
	;inc bl
	cmp al,07h
	ja big_com
Small_com:
	mov cl,al
	mov ch,00h
	mov ah,01h
	cmp cx,00h
	ja loop_com1;如果是0的话不能进入左移
	jmp end_com1
Loop_com1:
	shl ah,01h
	loop loop_com1
End_com1:
	mov dl,current_up_led
	test dl,ah
	jnz stop1
	jmp save
Big_com:
	mov cl,al
	sub cl,08h
	mov ch,00h
	mov ah,01h
	cmp cx,00h
	ja loop_com2;如果是0的话不能进入左移
	jmp end_com2
Loop_com2:
	shl ah,01h
	loop loop_com2
End_com2:
	mov dl,current_down_led
	test dl,ah
	jnz stop1
;-------------------------------------------
save:
	mov Outside[bx],al;将数据记录到seq中去
	inc out_num;out_num的数值自增1
	jmp stop1
lights_out:
	mov Out_num,00h
	mov al,00h;这里不应该只对应显示功能在后面的停止电梯中页应该用到
	mov dx,Fourth_A 
	out dx,al
	mov dx,Fourth_B
	out dx,al
	jmp stop1
stop1:
	pop bx
	pop dx
	ret
Xiaodou2 endp

Disp2 proc near;数码管显示部分
	push bx
	cmp al,08h;将al和08h进行比较
	jb up;up范围为00-07
	jmp down;down范围为08-0f
Up:
	mov bl,al
	mov bh,00h
	mov al,LED[bx]
	mov ah,current_up_led
	or al,ah
	mov current_up_led,al;更新上升数据
	mov dx,Fourth_A
	out dx,al
	jmp stop1
Down:
	sub al,08h
	mov bl,al
	mov bh,00h
	mov al,LED[bx]
	mov ah,current_down_led
	or al,ah
	mov current_down_led,al;更新上升数据
	mov dx,Fourth_B
	out dx,al
Stop1:
	pop bx
	ret
Disp2 endp
;--------------------------------------------------
Stop_running proc near;这里要使用中断不然不能不能使用
	mov num,00h
	mov out_num,00h
	mov ordered_num,00h
	mov al,00h;这里不应该只对应显示功能在后面的停止电梯中页应该用到
	mov dx,First_A
	out dx,al
	mov dx,Fourth_A
	out dx,al
	mov dx,Fourth_B
	out dx,al
	mov current_led,al
	mov current_up_led,al
	mov current_down_led,al
	ret
stop_running endp

Wait_5s proc near;软件延时程序
	push cx
	push ax
	mov cx,0ffh
D1:
	mov ax,0fffh
D2:
	dec ax
	jnz d2
	loop d1
	pop ax
	pop cx
	ret
Wait_5s endp
;-------------------------------------------------------
Process proc near;分四种情况,改变seq中数据存放的顺序
	push ax
	push bx
	push cx
	push dx
;-----------------------------------------------
Next_pro:;显示原始数据
;-----------------------------------------------
	;先对其进行正序排序
	;--------------------------------------
	lea di,seq
	mov cl,num;cx为比较轮数,大循环次数
	mov ch,00h
	mov sort_dir,00h;选择正向排序
	call bubble
	;--------------------------------------
	lea di,outside
	mov cl,out_num;cx为比较轮数,大循环次数
	mov sort_dir,00h;选择正向排序
    call Bubble  
;-----------------------------------------------
;判断最大值部分
;还有一种情况内部没有按键的情况根据外面的按键确定也是上优先于下
	mov bl,num
	mov bh,00h
	cmp bx,00h
	je find_out_max
	dec bx
	mov al,seq[bx]
	mov max,al
	jmp max_dir
Find_out_max:
	;分成两部分一部分是上行楼层的最大值,一部分是下行楼层的最大值
	mov bl,out_num
	mov bh,00h
	cmp bx,00h;两个数组都为0,直接结束
	je end_pro
	dec bx
	;---------------------------------------------
	;ah中保存下行楼层的最大值
	mov ah,outside[bx];最后一个元素不一定是下行的最大值,比如没有下行的情况就是上行的最大值
	cmp ah,09h
	jb down_max
	sub ah,08h
	;---------------------------------------------
	;dl中保存下行楼层的最大值,还需要考虑没有下行的情况
Down_max:
	inc bx
	mov cx,bx
	mov dl,00h;如果没有元素的话可以保存
	lea di,outside
Loop_find:
	mov al,[di]
	cmp al,09h;如果没有下行的情况直接就退出了
	jae compare_up_down;超出范围了
	mov dl,[di]
	inc di
	loop loop_find
Compare_up_down:
	cmp ah,dl
	jb up_big
	mov max,ah
	jmp max_dir
Up_big:
	mov max,dl
;-----------------------------------------------
;加入的重要的判断运行方向部分
;判断当前楼层是否大于最大值,下行
;判断当前楼层是否小于最小值,上行
;如果最大值等于当前楼层也是要下行的因为已经派出了只有一个目标楼层的情况
;其实只需要判断一种情况即下行的情况即可其余都为上行
Max_dir:
	mov al,max
	mov ah,current_floor
	dec ah;current_floor要改为从0开始
	cmp ah,al
	jae down_running
	jmp up_running
down_running:;确定运行方向上行
	mov al,00h
	mov dir,al;改变dir,00h表示下行 
	jmp Down_sorted
up_running:;确定运行方向下行
	mov al,01h
	mov dir,al;改变dir,01h表示上行
;-----------------------------------------------
;要先对其进行拍好顺序
Up_sorted:;正方向的排序
;a.内部按键大于当前楼层和外部按键高于当前楼层上行
;b.所有外部按键的下行和内部按键低于当前楼层
;c.外部按键低于当前楼层的上行
;-----------------------------------------------
;a.内部按键大于当前楼层和外部按键高于当前楼层上行
	;排序Seq中的数据
	lea di,seq;di表示内部按键
	lea si,tmp;si表示临时存储空间
	mov cl,num
	mov ch,00h
	cmp cx,00h
	je up_out_a
	mov al,current_floor
	dec al
Loop_seq_up1:
	cmp al,[di]
	ja back_seq_up_loop1;大于的话不加入
	mov ah,[di]
	call add_to_tmp;小于等于皆可
Back_seq_up_loop1:
	inc di
	loop Loop_seq_up1
	;排序Outside中的数据,并且必须是上行方向,自动满足了
Up_out_a:
	lea di,Outside
	mov cl,out_num
	mov ch,00h
	cmp cx,00h
	je end_up_a
	mov al,current_floor
	dec al
Loop_out_up1:
	cmp al,[di];al=04h
	ja back_out_up_loop1
	mov ah,09h
	cmp ah,[di]
	jbe back_out_up_loop1;09h小于等于[di]说明是下行0不加入
	mov ah,[di]
	call add_to_tmp;要小于09h且大于等于current_floor的值
Back_out_up_loop1:
	inc di
	loop Loop_out_up1
	;--------------------------------------
	;先对ordered中的数据进行排序
	lea di,tmp
	mov cl,tmp_num;cx为比较轮数,大循环次数
	mov sort_dir,00h;选择正向排序
	call bubble
	;--------------------------------------
	;再删除排好序之后重复的元素
	lea di,tmp
	mov cl,tmp_num;cx为比较轮数,大循环次数
	call delete_repeat
	mov tmp_num,dh
	;--------------------------------------
End_up_a:
	call move_tmp_order;需要进行排序排序以后去除重复的元素
	call clear_tmp
;--------------------------------------------------
;b.所有外部按键的下行和内部按键低于当前楼层
	lea di,seq
	lea si,tmp
	mov cl,num
	mov ch,00h
	cmp cx,00h
	je up_out_b
	mov al,current_floor
	dec al
Loop_seq_up2:
	cmp al,[di]
	jbe back_seq_up_loop2;大于等于的话不加入
	mov ah,[di]
	call add_to_tmp;小于加入
Back_seq_up_loop2:
	inc di
	loop Loop_seq_up2
	;排序Outside中的数据,加入所有方向向下的元素
Up_out_b:
	lea di,Outside
	mov cl,out_num
	mov ch,00h
	cmp cx,00h
	je end_up_b
Loop_out_up2:
	mov al,[di]
	cmp al,09h
	jb back_out_up_loop2;小于09h的都是上行,不加入
	sub al,08h;上行因此要减去09h
	mov ah,al
	call add_to_tmp;加入所有下行元素
Back_out_up_loop2:
	inc di
	loop Loop_out_up2	
	;--------------------------------------
End_up_b:;如果只有一个元素有的话只需要倒序,其实不需要去除重复
	lea di,tmp
	mov cl,tmp_num;cx为比较轮数,大循环次数
	mov sort_dir,01h;选择正向排序
	call bubble
	;;--------------------------------------
	lea di,tmp
	mov cl,tmp_num;cx为比较轮数,大循环次数
	call delete_repeat
	mov tmp_num,dh
	;--------------------------------------
	call move_tmp_order
	call clear_tmp
;--------------------------------------------------
;c.外部按键低于当前楼层的上行
	;外部按键低于当前楼层的上行
	lea di,Outside
	mov cl,out_num
	mov ch,00h
	cmp cx,00h
	je end_up_c
	mov al,current_floor
	dec al
Loop_out_up3:
	cmp [di],al
	jae back_out_up_loop3
	mov ah,[di]
	call add_to_tmp;小于al且上行
Back_out_up_loop3:
	inc di
	loop Loop_out_up3
	call move_tmp_order
	call clear_tmp
	;--------------------------------------
	lea di,ordered
	mov cl,ordered_num;cx为比较轮数,大循环次数
	call delete_repeat
	mov ordered_num,dh
End_up_c:
	jmp end_pro
;--------------------------------------------------	
Down_sorted:;先向下运行的结果
;a.内部按键小于当前楼层和外部按键低于当前楼层下行
;b.所有外部按键的上行
;c.外部按键高于当前楼层的下行
;--------------------------------------------------	
;a步骤
	;排序Seq中的数据
	lea di,seq;di表示内部按键
	lea si,tmp;si表示临时存储空间
	mov cl,num
	mov ch,00h
	cmp cx,00h
	je down_out_a
	mov al,current_floor
	dec al
Loop_seq_down1:;不需要比较全部加入
	mov ah,[di]
	call add_to_tmp
	inc di
	loop Loop_seq_down1
	;排序Outside中的数据,并且必须是下行方向,且低于当前楼层
Down_out_a:	
	lea di,Outside
	mov cl,out_num
	mov ch,00h
	cmp cx,00h
	je end_down_a
	mov al,current_floor
	dec al
	add al,08h;要加上08h才可以比较
Loop_out_down1:
	mov dl,[di]
	cmp dl,09h
	jb back_out_down_loop1;小于09h表示上行
	cmp al,[di];只加入小于等于当前楼层的楼层
	jb back_out_down_loop1
	mov ah,[di]
	sub ah,08h
	call add_to_tmp;要小于09h且大于等于current_floor的值
Back_out_down_loop1:
	inc di
	loop Loop_out_down1
End_down_a:
	;--------------------------------------
	;先对ordered中的数据进行排序
	lea di,tmp
	mov cl,tmp_num;cx为比较轮数,大循环次数
	mov sort_dir,01h;选择反向排序
	call bubble
	;--------------------------------------
	;再删除排好序之后重复的元素
	lea di,tmp
	mov cl,tmp_num;cx为比较轮数,大循环次数
	call delete_repeat
	mov tmp_num,dh
	;--------------------------------------
	call move_tmp_order;需要进行排序排序以后去除重复的元素
	call clear_tmp
;--------------------------------------------------
;b步骤
;所有外部按键的上行
	lea di,Outside
	mov cl,out_num
	mov ch,00h
	cmp cx,00h
	je end_down_b
Loop_out_down2:
	mov al,[di]
	cmp al,09h
	jae back_out_down_loop2;小于09h的都是下行,不加入
	mov ah,al
	call add_to_tmp;加入所有下行元素
Back_out_down_loop2:
	inc di
	loop Loop_out_down2	
	;--------------------------------------
	call move_tmp_order
	call clear_tmp
End_down_b:
;--------------------------------------------------	
;c步骤
	;外部按键高于当前楼层的下行
	lea di,Outside
	mov cl,out_num
	mov ch,00h
	cmp cx,00h
	je end_down_c
	mov al,current_floor
	dec al
	add al,08h
Loop_out_down3:
	cmp [di],al
	jbe back_out_down_loop3;大于al且下行
	mov ah,[di]
	sub ah,08h
	call add_to_tmp;小于al且上行
Back_out_down_loop3:
	inc di
	loop Loop_out_down3
	;--------------------------------------
	;先对tmp中的数据进行反序
	lea di,tmp
	mov cl,tmp_num;cx为比较轮数,大循环次数
	mov sort_dir,01h;选择反向排序
	call bubble
	;--------------------------------------
	call move_tmp_order;移动
	call clear_tmp
	;--------------------------------------
	lea di,ordered;ordered整体去重
	mov cl,ordered_num;cx为比较轮数,大循环次数
	call delete_repeat
	mov ordered_num,dh
	;--------------------------------------
End_down_c:
	jmp end_pro
end_pro:   
;------------------------------------------ 
;测试显示部分
	;mov row,01h;设置行
   	;mov line,00h;设置列
   	;mov bx,00h
   	;mov al,ordered[bx]
   	;call show_number
   	;mov cl,ordered_num
   	;mov ch,00h
   	;lea di,ordered
;Loop_ascii:
	;mov al,[di]
	;add al,'0'
	;mov [di],al
	;inc di
	;loop loop_ascii
	;
   	;lea di,ordered
   	;mov cl,ordered_num
   	;call show_string
   	;jmp $
;--------------------------------------------
	pop dx
	pop cx
	pop bx
	pop ax
	ret
Process endp
	
Add_to_tmp proc near;加入tmp中
	mov [si],ah
	inc si
	inc tmp_num
	ret
Add_to_tmp endp

Clear_tmp proc near;清除
	mov tmp_num,00h
	lea si,tmp
	ret
Clear_tmp endp

Move_tmp_order proc near;将tmp中的数据转移到ordered中保存
	mov cl,tmp_num
	mov ch,00h
	cmp cx,00h;tmp中没有元素不需要转移
	je exit
	mov bl,ordered_num
	mov bh,00h
	lea si,tmp
Loop_move:
	mov al,[si]
	mov ordered[bx],al
	inc bx
	inc si
	inc ordered_num
	loop loop_move
exit:
	ret
Move_tmp_order endp
;-----------------------------------------------
Bubble proc near;冒泡排序
	cmp cx,01h;小于等于1说明不需要使用冒泡排序
	jbe end_bubble
	mov si,di;备份di中的数据
    ;冒泡排序部分
    mov ch,00h
    dec cx
Loop_sort1:
	mov dx,cx;dx表示大循环次数
	;mov bx,0;地址指针
	;lea di,seq
	mov di,si
Loop_sort2:
	;mov al,seq[bx]
	mov al,[di]
	;cmp al,seq[bx+1]
	cmp sort_dir,01h
	je ir_dir
	cmp al,[di+1]
	jbe no_change
Loop_back:
	;xchg al,seq[bx+1]
	xchg al,[di+1]
	;mov seq[bx],al;在这里相当于溢出多给num了值
	mov [di],al
no_change:
	;inc bx
	inc di
	loop loop_sort2
	mov cx,dx;一轮比完之后,cx再次赋值比较轮数
	loop loop_sort1
	jmp end_bubble
Ir_dir:
	cmp al,[di+1]
	jae no_change
	jmp Loop_back
End_bubble:
	ret
Bubble endp	

Delete_repeat proc near;数据放在di中,数据长度放在cl中
	cmp cl,01h;排除只有无元素和一个元素的情况
	jbe end_delete
	mov dh,cl;备份一下数据,表示元素的个数
	dec cl;真实比较的次数要比实际元素的个数少一个
Loop_delete:
	mov al,[di]
	cmp al,[di+1]
	je delete
Back_loop:;将后面的元素都往前移动
	inc di
	loop loop_delete
	jmp exit
Delete:;如果相等的是最后一个元素的情况
	cmp cl,01h
	je End_repeat;说明重复的是倒数两个元素
	dec dh
	push cx
	push di
	dec cx
	;mov cx,01h
Loop_delete2:
	mov dl,[di+2]
	mov [di+1],dl
	inc di
	loop loop_delete2
	pop di
	pop cx
	dec cx
	jmp back_loop
End_repeat:
	dec dh;不需要dec cx了因为已经不会再进行比较了
	ret
End_delete:
	mov dh,cl
Exit:
	ret
Delete_repeat endp
;-------------------------------------------------------
Disp proc near;数码管显示部分
	push cx
	push bx
	mov cl,num;元素的个数放入cl中
	mov ch,00h;为后面的loop做准备
	mov bh,00h;为后面的bx做序号做准备
	lea di,seq;seq的取值为0到7
	lea si,led;输出的数据应该是led中的数据
	mov al,00h;为之后的与运算做好准备
	cmp cl,00h
	je stop2
	cmp cl,03h
	ja lights_out
Loop1:
	mov bl,[di];求得当前的序号,下表
	or al,led[bx];由下标值求得对应的元素值
	inc di
	loop loop1
	mov dx,First_A
	out dx,al
	mov current_led,al;保存上一次输出的数据为熄灭做准备
	jmp stop2
lights_out:
	mov num,00h
	mov al,00h;这里不应该只对应显示功能在后面的停止电梯中页应该用到
	mov dx,First_A
	out dx,al
stop2:
	pop bx
	pop cx
	ret
Disp endp

delay proc near;软件延时程序
	push cx
	push ax
	mov cx,011h
D1:
	mov ax,00ffh
D2:
	dec ax
	jnz d2
	loop d1
	pop ax
	pop cx
	ret
delay endp
;-------------------------------------------------
Lcd_main proc near
    call Lcd_init
restart:
   	mov row,00h;设置行
   	mov line,03h;设置列
   	lea di,buf1;显示'Welcome'
   	mov cl,length1
   	call show_string	
    ret
Lcd_main endp

Set_cursor proc near
	push bx
	mov bl,line
	mov bh,row
	cmp bh,00h
	ja row_2;大于说明再第二行
Row_1:
	add bl,00h
	or bl,80h
	mov al,bl
	jmp stop	
Row_2:
	add bl,40h
	or bl,80h
	mov al,bl
Stop:
	pop bx
	ret
Set_cursor endp

Show_string_clear proc near;数据在di中,长度在cl中
	push ax
	push bx
	push cx
	mov ch,00h
	mov cl,05h
	call set_cursor;得到的al的结果即为要显示的数据的位置
   	call Lcd_wcmd
;------------------------------------
	mov cl,05h
Loop_clear:
	mov al,' '
	call Lcd_wdata
	loop Loop_clear
	pop cx
	pop bx
	pop ax
	ret
show_string_clear endp

Show_string proc near;数据在di中,长度在cl中
	push ax
	push bx
	mov ch,00h
	;push cx;将cl压栈
	call set_cursor;得到的al的结果即为要显示的数据的位置
   	call Lcd_wcmd
;------------------------------------
	;mov cl,05h
;Loop_clear:
	;mov al,' '
	;call Lcd_wdata
	;loop Loop_clear
	;pop cx;将cl出栈
	;call set_cursor;重新定位
   	;call Lcd_wcmd
;------------------------------------
Loop1:
	mov al,[di]
	call Lcd_wdata
	inc di
	loop Loop1
	pop bx
	pop ax
	ret
show_string endp

Show_number proc near;根据当前al中的数据判断显示什么数据
	push ax
	mov ah,'0'
	add al,ah
	;mov row,00h;设置行
   	;mov line,02h;设置列
   	mov [di],al
   	mov cl,01h
   	call show_string
   	pop ax
	ret
show_number endp

Lcd_init proc near;初始化函数
	mov dx,Second_MODE
    mov al,88h;全为输出,C的高位设置成输入模式
    out dx,al
	mov al,38h;16*2显示,5*7点阵,8位数据接口
	call Lcd_wcmd     
	mov al,0ch;显示器开,光标关闭
	call Lcd_wcmd
	mov al,06h;文字不动,地址自动+1
	call Lcd_wcmd
	mov al,01h;清屏
	call Lcd_wcmd
	ret
Lcd_init endp

Lcd_wcmd proc near;写命令函数,把命令存储在al中
	call waitready	
	mov bl,00h;RS和RW都为低电平表示命令模式,输出
	call RW_RS_set
	mov bl,01h;先需要设置E为低电平
	call E_set
	mov dx,Second_A
	out dx,al
	mov bl,00h;再设E为高电平
	call E_set
	ret
Lcd_wcmd endp

Lcd_wdata proc near;写数据函数,把数据存储在al中
	call waitready
	mov bl,01h;RS=1,RW=0表示数据模式,输出
	call RW_RS_set
	mov bl,01h;先需要设置E为低电平
	call E_set
	mov dx,Second_A
	out dx,al
	mov bl,00h;再设E为高电平
	call E_set
	ret
Lcd_wdata endp

Waitready proc near
	push ax
	mov dx,Second_A;先输出A口为0ffh
	mov al,0ffh
	out dx,al
	mov bl,02h;RS=0,RW=1表示命令模式,输入
	call RW_RS_set
Loop1:
	mov bl,01h
	call E_set
	mov dx,Second_C;从C口读入命令字
	in al,dx
	test al,80h;与运算判断输入的第八位是否为1
	mov bl,00h
	call E_set
	jnz loop1;高位为1表示还没有处理完
	pop ax
	ret
Waitready endp

RW_RS_set proc near;对应PB0,PB1,先把数据存在bl中
	;00表示RW=RS=0,01表示RW=0,RS=1,02表示RW=1,RS=0
	push ax
	mov dx,Second_B
	mov al,bl
	out dx,al
	pop ax
	ret
RW_RS_set endp

E_set proc near;对应PC4,先把数据存在bl中
	push ax
	mov dx,Second_C
	mov al,bl
	out dx,al
	pop ax
	ret
E_set endp
;-------------------------------------------------------
Timer proc near;产生2ms脉冲信号
	push ax
	mov dx,MY8253_MODE     
	mov al,076h;01通道1,11先读写低字节后读写高字节,011方式3,0二进制计数
	out dx,al
	mov dx,MY8253_COUNT1
	mov ah,0f4h;频率为1kHz,计数值为500,高位为01,低位为f4
	mov al,ah
	out dx,al
	mov al,01h
	out dx,al
	pop ax
	ret
Timer endp

Play_music proc near;停止计数器的计数
FREQ:
	mov di,[si]
	cmp di,0
	je stop
	
	call sound
	add si,2
	add bx,2
	jmp freq
Stop:
	mov dx,Third_c
	mov al,00h;停止播放音乐
	out dx,al
	ret
Play_music endp

Sound proc near
	mov dx,MY8253_MODE
	mov al,0b6h;选择通道2,11先读写低字节后读写高字节,011方式3,0二进制计数
	out dx,al
	mov dx,2
	mov ax,307
	div di
	mov dx,MY8253_COUNT2
	out dx,al
	mov al,ah
	out dx,al
	mov ax,[bx]
sloop:
	mov cx,0018h
	loop $
	dec ax
	jnz sloop
	ret
Sound endp
;-------------------------------------------------------
Running proc near
	push bx
	push cx
	mov row,01h
   	mov line,04h  	
   	lea di,buf6;显示'Run'
   	mov cl,length6
   	call show_string_clear
   	call show_string
   	
	mov dx,Third_mode
	mov al,10000010B;A口输出,B口输入,C口第四位输出
	out dx,al;设置当前8255的模式
	mov al,current_floor;al中存储当前楼层
	mov ah,aim_floor;ah中存储目标楼层
	cmp al,ah
	;-----------------------------------------
	;mov al,ah
	;call show_number
	;jmp $
	;-----------------------------------------
	ja down;当前楼层大于目标楼层下行
	jb up;当前楼层小于目标楼层上行
	je equal
Down:
	mov dl,00h
	mov current_dir,dl
	mov row,00h
   	mov line,0bh
   	lea di,buf3;显示'Down'
   	mov cl,length3
   	;call show_string_clear
   	call show_string
   	
	mov dx,Third_A;输出初始值即楼层到计数器
	out dx,al
	mov dx,Third_C
	mov al,03h;PC0=1因为要做减法计数器
	out dx,al
	call Timer;开始运行
Down_loop:
	mov dx,Third_B
	in al,dx
	;---------------------------------------------
	;mov ah,03h
	;mov stop_mode,ah
	;call stop_running
	;---------------------------------------------
	cmp al,ah;仍然大于目标楼层
	ja down_loop
	jmp stop
Up:
	mov dl,01h
	mov current_dir,dl
	mov row,00h
   	mov line,0bh
   	lea di,buf2;显示'Up'
   	mov cl,length2	
   	call show_string
   	
	mov dx,Third_A;输出初始值即楼层到计数器
	out dx,al
	mov dx,Third_C
	mov al,02h;PC0=0因为要做加法计数器
	out dx,al
	call Timer
Up_loop:
	mov dx,Third_B
	in al,dx	
	;---------------------------------------------
	;mov ah,02h
	;mov stop_mode,ah
	;call stop_running
	;---------------------------------------------
	cmp al,ah;仍然小于目标楼层
	jb up_loop
	jmp stop
Equal:
	mov dx,Third_A
	out dx,al
Stop:
	mov dx,Third_C
	mov al,04h;开始播放音乐的同时停止0.5s脉冲的输出
	out dx,al
	mov Current_floor,ah;更新当前楼层
	mov al,ah
	;显示部分
	mov row,01h
   	mov line,04h
   	lea di,buf5;显示'Open'
   	mov cl,length5
   	call show_string
   	;------------------------------------
   	;播放乐曲部分
   	mov si,offset tfreq
	mov bx,offset tdly
	call Play_Music
	mov row,00h
   	mov line,0bh
   	call show_string_clear;清空up和down
   	;-------------------------------------
	pop cx
	pop bx
	ret
Running endp
;-------------------------------------------------------
    MOV AH,4CH
    INT 21H
    

CODES ENDS
    END START

电梯图纸

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值