本文针对:北邮微机原理硬件实验一,实验平台介绍和实验一IO的使用。用基于8086的汇编进行编程实验。
前半学期荒废,基本没学汇编语言,看着实验要求就一个头痛。
同学找来学长代码,成功救赎。
写下本文,以膜拜学长,并从中学习微原硬件实验编程方法。
实验要求
1.学习使用Debug命令,并用I、O命令直接对端口进行读写操作,
2.用汇编语言编写跑马灯程序。(使用EDIT编辑工具)实现功能
A.通过读入端口状态(ON为低电平),选择工作模式(灯的闪烁方式、速度等)。
B.通过输出端口控制灯的工作状态(低电平灯亮)
学长的代码
我就直接把学长的代码粘贴过来啦,保留了作者信息,学长会原谅我的。
; AUTHOR : XIAO, Zhiqing ( No. 13 , Cl. 07105 )
; DATE : 20091209
; MODIFIED: feichashao, 2013-12-7
; DESCRIPTION : revolving scenic lamps which shift in either direction
; USAGE : switchers -- ( SW0 is the most left one )
; SW0 ( play ) : UP exit DOWN play
; SW1 ( direction ): UP right DOWN left
; SW2 ( pause ) : UP pause DOWN play
; SW3 ( speed ) : UP slow DOWN fast
; SW4 ( mode ) : UP three lights DOWN one light
Data SEGMENT
SwState DB 0
LdState DB 0FEH
Buffer DB 0
Data ENDS
Stack SEGMENT STACK 'STACK'
DB 100H DUP(0)
Stack ENDS
Code SEGMENT
ASSUME DS:Data,SS:Stack,CS:Code
Main PROC
PUSH DS
XOR AX,AX
PUSH AX
MOV AX,Data
MOV DS,AX
MOV ldState,0FEh;initialize
CALL EndlessRepeat
RET;unreachable instruction
Main ENDP
EndlessRepeat PROC
EndlessLoop: CALL ProcToRepeat
JMP EndlessLoop
RET
EndlessRepeat ENDP
ProcToRepeat PROC
CALL readSw
CALL excuteDirection
CALL excuteMode
CALL excutePause
CALL excuteReturnToDos
CALL excuteSpeed
RET
ProcToRepeat ENDP
DivFreq PROC
PUSH CX
MOV CX,1000H
MyLoop: PUSH CX
MOV CX,8000H
MyNestedLoop:DEC CX
JNZ MyNestedLoop
POP CX
DEC CX
JNZ MyLoop
POP CX
RET
DivFreq ENDP
ReadSw PROC
PUSH AX
PUSH DX
MOV DX,0E8E0H
IN AL,DX
MOV SwState,AL
POP DX
POP AX
RET
ReadSw ENDP
WriteLd PROC
PUSH DX
PUSH AX
MOV AL,LdState
MOV DX,0E8E0H
OUT DX,AL
POP AX
POP DX
RET
WriteLd ENDP
excuteReturnToDos PROC
TEST SWSTATE,01H
JZ exitexcuteReturnToDos
CALL returnToDos
exitexcuteReturnToDos:RET
excuteReturnToDos ENDP
ReturnToDos PROC
MOV AX,4C00H
INT 21H
ReturnToDos ENDP
excuteDirection PROC
TEST SWSTATE,02H
JZ shiftLeft
ROR LDSTATE,01H
ROR LDState,01H
shiftLeft:ROL LDSTATE,01H
exitexcuteDirection:RET
excuteDirection ENDP
excutePause PROC
beginexcutePause:CALL readSw
TEST swState,04H
JNZ beginexcutePause
RET
excutePause ENDP
excuteSpeed PROC
CALL divFreq
TEST swState,08H
JZ exitexcuteSpeed
CALL divFreq
CALL divFreq
exitexcuteSpeed: RET
excuteSpeed ENDP
excuteMode PROC
PUSH WORD PTR ldState
TEST swState,10H
JZ simpleMode
PUSH AX
PUSH BX
PUSH CX
MOV AL,ldState
MOV BL,AL
ROL BL,1
AND BL,AL
MOV CL,BL
ROL CL,1
AND AL,CL
MOV ldState,AL
POP CX
POP BX
POP AX
simpleMode: CALL writeLD
exitexcuteMode: POP WORD PTR ldState
RET
excuteMode ENDP
Code ENDS
END Main
程序结构,伪代码
因为坑爹的实验要在DOS环境下敲代码,老师又各种催,所以注释没有加上。不过学长的变量和函数命名都相当清晰,相信大家一看就能懂。
程序以main为入口,跟一般高级语言编程是一个思路,太喜欢啦~
我就把程序的大致框架用伪代码写一下:
{
initialize();
while(1){
read_switch_state(); // Read the physical switches state.
execute_direction(); // Change the marquee direction according to the switches state read.
execute_mode(); // Change mode(Single/Triple Flashing) according to the switches state read.
execute_pause(); // Pause if the switches state acquires.
execute_return_to_dos(); // Return to DOS if the switches state acquires.
excute_speed(); // Delay. The latency is determined by the switches state read.
}
return 0;
}
类似函数的分块法
仔细品读了学长的代码,我又学会了汇编语言是怎样给程序分块的,也就是说写各种不同的“函数”,然后调用。
所有代码都在Code Segment里面写。“函数”(Process)是这样定义的:
一些操作
RET
(过程名) ENDP
在“函数”(Process)的开始,一般会用栈保存寄存器的数据,操作结束的时候再恢复,这样函数就不会搞乱整个程序了。
汇编中使用变量
单靠寄存器,没有变量实在没法活呀。
哈哈,学长的代码就有“变量”的定义和使用方法,以后可以学着用。
留意到,代码里的变量都是直接定义在Data Segment,直接在程序里使用变量名就可以对变量进行读写等操作。
SwState DB 0
LdState DB 0FEH
Buffer DB 0
Data ENDS
要注意的是,程序初始化的时候,一定要把DS指向Data Segment,否则变量是无法使用的!切记!
XOR AX,AX
PUSH AX
MOV AX,Data
MOV DS,AX
好啦,就写这么多啦,详情见代码吧。