📄 jjztl.asm
字号:
;**********************************************************
;* 文 件 名: JJZTL.asm *
;* 说 明: IO程序 - 交通灯演示程序 *
;* 作 者: 蒋露 计超 张业 *
;* 创建日期: 2006.4.11 *
;* 编译工具: MASM 5.0/6.0 *
;* 备 注: *
;* *
;* 0. 主程序结构: 本程序由主循环和定时器中断处理函数组成, *
;* 程序首先初始化IRQ7号中断的处理函数为NewIRQ7,再初始 *
;* 化8255的PA,PC口为方式0输出,再初始化8253为IRQ7中断源 *
;* ,中断频率为10次/秒,然后进入主循环.主循环负责显示由 *
;* 全局变量CurTime保存的十位和个位当前倒计时数还有由全 *
;* 局变量CurMode保存的当前LED灯显示状态(还包括S1和S0). *
;* 当定时器中断发生时会调用NewIRQ7中断处理函数,该函数 *
;* 进行计秒处理(由SecTick记录),当到达一秒时调用DecTime *
;* 函数进行对CurTime的递减操作.NewIRQ7还进行显示模式的 *
;* 判断,ModeInt记录了14个模式各自要经过中断的次数(模式 *
;* 0的数据为300就是说明模式0要经过300个中断即30秒才跳 *
;* 转到下一个模式).TimeInc记录了模式转换时在CurTime上 *
;* 的增量.这是一个小小的技巧,用来避免大量的跳转指令,但 *
;* 愿这不会使我的代码看起来更加困难. ^_^. *
;* *
;* *
;* 1. 退出按键检测问题: 这大概是本程序中最令人困扰的了,有 *
;* 一个小函数KeyTest用来测试是否该退出,当它用AL返回1时 *
;* 代表可以退出.但关键是这个代码怎么写.KeyTest必须是无 *
;* 等待的,它不可以打断主显示循环的不断执行.DOS中断06H *
;* 和BIOS的16号中断都可以不暂停的执行按键检测任务.但是 *
;* 测试结果是他们总会造成莫名其妙的程序异常.问题肯定出 *
;* 在中断嵌套上,他们内部肯定会使用STI指令打开中断的,这 *
;* 时如果IRQ7中断到来,就会有问题.而且为了定时器的准确 *
;* 最好不要随便屏蔽IRQ7.暂时的解决方案是使用外部开关K0 *
;* 连线方法详见实验报告的实验一图2(74LS244). *
;* *
;* 2. 优化和改进: 多个计数变量的合并,代码设计时很随意,没 *
;* 有仔细考虑变量的共同点,比如CurTime和ModeTick就可以 *
;* 合并.中断粒度的减小,当前中断间隔为0.1秒,其实由于模 *
;* 间的最小间隔是0.5秒,所以可以将中断间隔延长到0.5秒. *
;* 退出机制的改进,当前采用了利用74LS244的K0开关控制程 *
;* 序,应该还可以直接读键盘缓冲的方式判断按键.(已改) *
;* *
;* 七段数码管连线示意图: *
;* a - PA0 S0 - PC0 *
;* b - PA1 S1 - PC1 a *
;* c - PA2 ━ *
;* d - PA3 dp - 地 f┃g ┃b *
;* e - PA4 ━ *
;* f - PA5 e┃ ┃c *
;* g - PA6 ━ *
;* d *
;* *
;* *
;* LED灯连线示意图: ○ ○ ○ ○ ○ ○ ○ ○ *
;* 1红 1黄 1绿 无 无 2红 2黄 2绿 *
;* PC7 PC6 PC5 地 地 PC4 PC3 PC2 *
;* *
;* 8255端口A设置: PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0 *
;* 空 g f e d c b a *
;* *
;* 8255端口C设置: PC7 PC6 PC5 PC4 PC3 PC2 PC1 PC0 *
;* 1红 1黄 1绿 2红 2黄 2绿 S1 S0 *
;* 1 - 南北方向 *
;* 2 - 东西方向 *
;* *
;* 中断部分连线参考实验报告实验2 *
;**********************************************************
; 需要使用到286指令 PUSHA/POPA,可以用多个PUSH/POP替换
.286
;----------------------------------------------------------
; 常量定义
E8255_PA EQU 288H
E8255_PC EQU 28AH
E8255_CTL EQU 28BH
E8253_CLK0 EQU 280H
E8253_CLK1 EQU 281H
E8253_CTL EQU 283H
E8259_CTL0 EQU 20H
E8259_CTL1 EQU 21H
; 键盘端口
KEYBOARD_PORT EQU 60H
; IRQ7的中断号
IRQ7_NR EQU 0FH
IRQ7_OPEN_MASK EQU 7FH
IRQ7_CLOSE_MASK EQU 80H
; 一秒中断数
SEC_SETUP EQU 10
;----------------------------------------------------------
; 数据段
DATA_SEG SEGMENT
; LED灯的显示模式,PC口输出
LedMode DB 00110000B ; 南北绿灯亮,东西红灯亮 0
DB 01010000B ; 南北黄灯亮,东西红灯亮 1
DB 00010000B ; 东西红灯亮 2
DB 01010000B ; 南北黄灯亮,东西红灯亮 3
DB 00010000B ; 东西红灯亮 4
DB 01010000B ; 南北黄灯亮,东西红灯亮 5
DB 00010000B ; 东西红灯亮 6
DB 10000100B ; 南北红灯亮,东西绿灯亮 7
DB 10001000B ; 南北红灯亮,东西黄灯亮 8
DB 10000000B ; 南北红灯亮 9
DB 10001000B ; 南北红灯亮,东西黄灯亮 10
DB 10000000B ; 南北红灯亮 11
DB 10001000B ; 南北红灯亮,东西黄灯亮 12
DB 10000000B ; 南北红灯亮 13
; 七段数码管段码表
SegTab DB 00111111B ; 3fh - 0
DB 00000110B ; 6h - 1
DB 01011011B ; 5bh - 2
DB 01001111B ; 4fh - 3
DB 01100110B ; 66h - 4
DB 01101101B ; 6dh - 5
DB 01111101B ; 7dh - 6
DB 00000111B ; 7h - 7
DB 01111111B ; 7fh - 8
DB 01101111B ; 6fh - 9
; 当前LED模式
CurMode DB 0
; 当前倒计数时间(十位和个位分开)
CurTime DB 1, 0
; 模式的Tick间隔
ModeInt DW 100, 5, 5, 5, 5, 5, 5
DW 100, 5, 5, 5, 5, 5, 5
; 模式的时间递增量
TimeInc DB 1, 0
DB 0, 3
DB 0, 0
DB 0, 0
DB 0, 0
DB 0, 0
DB 0, 0
DB 1, 0
DB 0, 3
DB 0, 0
DB 0, 0
DB 0, 0
DB 0, 0
DB 0, 0
; 模式计数(倒计数,用来记录当前模式剩余中断次数)
ModeTick DW 0
; 秒计数(用来计秒数)
SecTick DB SEC_SETUP
WelMsg DB 'Press ESC to quit.', 13, 10, '$'
; 保存的旧中断向量
OldIrq DW ?, ?
DATA_SEG ENDS
;----------------------------------------------------------
; 堆栈段
STACK_SEG SEGMENT STACK
DW 128 DUP(?)
STACK_SEG ENDS
;----------------------------------------------------------
; 代码段
CODE_SEG SEGMENT
ASSUME DS:DATA_SEG, SS:STACK_SEG, CS:CODE_SEG
;----------------------------------------------------------
; 主程序
;┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
;┃MAIN 主程序 ┃
;┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
MAIN PROC FAR
START:
MOV AX, DATA_SEG
MOV DS, AX
MOV AX, STACK_SEG
MOV SS, AX
; 显示欢迎字符串
LEA DX, WelMsg
MOV AH, 9H
INT 21H
; 设置新的中断向量
CALL SetIRQ
; 初始化8255
CALL Init8255
; 初始化8253
CALL Init8253
; 主程序循环
M_LOOP:
MOV BH, [CurTime]
MOV BL, [CurTime+1]
; 显示十位
MOV AH, 2H
MOV AL, BH
CALL Display
CALL Delay
; 显示个位
MOV AH, 1H
MOV AL, BL
CALL Display
CALL Delay
; 判断按键
CALL KeyTest
CMP AL, 1
JNE M_LOOP
; 恢复旧中断向量
CALL RestoreIRQ
; 返回DOS
MOV AH, 4CH
MOV AL, 0H
INT 21H
MAIN ENDP
;----------------------------------------------------------
;┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
;┃Init8253 初始化 8253 ┃
;┣━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
;┃参 数 : 无 ┃
;┃返 回 值 : 无 ┃
;┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Init8253 PROC NEAR
PUSHA
;向8253写控制字
MOV DX, E8253_CTL
;控制字:计数器0,方式3,16位操作,二进制模式
MOV AL, 00110110B
OUT DX, AL
;写入计数器0初值1000
MOV AX, 1000
;先写入低字节
MOV DX, E8253_CLK0
OUT DX, AL
;后写入高字节
MOV AL, AH
OUT DX, AL
;向8253写控制字
MOV DX, E8253_CTL
;控制字:计数器1,方式2,16位操作,二进制模式
MOV AL, 76H
OUT DX, AL
;写入计数器1初值100
MOV AX, 100
;先写低字节
MOV DX, E8253_CLK1
OUT DX, AL
;后写高字节
MOV AL, AH
OUT DX, AL
POPA
RET
Init8253 ENDP
;----------------------------------------------------------
;┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
;┃Init8255 初始化 8255 ┃
;┣━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
;┃参 数 : 无 ┃
;┃返 回 值 : 无 ┃
;┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Init8255 PROC NEAR
PUSHA
;使8255的PA,PC口为方式0输出
MOV DX, E8255_CTL
MOV AL, 10000000B
OUT DX, AL
POPA
RET
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -