Modbus背景及程序框架
两路差分输入信号PG1、PG2经输入驱动,转化为单端输入MA、MB;板载14路二选一选通开关;以第i路为例,若选通信号Si=1,i路选通输出OZi=MA;否则,选通输出OZi=MB;最终,OZi经输出驱动重新转化为差分信号Zi +,Zi-输出。各路选通信号Si ,由51芯片的P2、P1口输出控制; 其它设备通过485串行接口,可发送Modbus命令帧读写Si值,实现信号的选择输出。
Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。
标准的Modbus口是使用一RS-232C兼容串行接口,它定义了连接口的针脚、电缆、信号位、传输波特率、奇偶校验。控制器能直接或经由Modem组网。
控制器通信使用主—从技术,即仅一设备(主设备)能初始化传输(查询)。其它设备(从设备)根据主设备查询提供的数据作出相应反应。程序中,该板卡作为从设备。
源程序及注释
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; P1.0--7,P2.0--5: SELECT BITS
BSEG
B_S15 BIT P1.0
B_S14 BIT P1.1
B_S13 BIT P1.2
B_S12 BIT P1.3
B_S11 BIT P1.4
B_S10 BIT P1.6
B_S2 BIT P2.0
B_S3 BIT P2.1
B_S4 BIT P2.2
B_S5 BIT P2.3
B_S6 BIT P2.4
B_S7 BIT P2.5
B_S8 BIT P1.5
B_S9 BIT P1.7
INTRANSMIT BIT 00H ; 20H ;D19发送状态标志位
HAVEDATAINBUF BIT 01H ;缓冲区接收完整数据保标志位
TRANSMITCTRL BIT P3.6
RECEIVECTRL BIT P3.7 ;D19方向选择,全0选择接收状态,全1选择发送状态
ENDS
DSEG
;使用寄存器1区
P1S_COUNTER = 0AH ; R2
MS_COUNTER = 0BH ; R3
IN_LASTSTABLE = 0CH ; R4
IN_CHANGEEVER = 0DH ; R5
INTERVALT = 0EH ; R6
F_NAP = 0FH ; R7
;使用寄存器2区
REC_DPOINTER = 10H ; R0
TRS_DPOINTER = 11H ; R1
RECBYTECNT = 12H ; R2
TRSBYTECNT = 13H ; R3
N_STATIONADDR = 15H ; R5 ;从机地址
BIT_MEMORY DATA 20H ; BIT DATA UNIT
PARASTARTADDR DATA 28H
CTRLWRD = PARASTARTADDR
TOPOFSTACK = 4FH
BOTTOMOFSTACK = 5FH ;堆栈范围
REC_STATIONADDR = 60H ;缓冲区首地址 存放从机地址
TRS_STATIONADDR= REC_STATIONADDR
REC_COMMAND = 61H ;功能码
REC_DATAADDRH = 62H ;寄存器地址高字节
EXCEPTIOCODE = REC_DATAADDRH
RETURNBYTECNT = REC_DATAADDRH
REC_DATAADDRL = 63H ;寄存器地址低字节
REC_DATACNTH = 64H ;寄存器写入值高字节
REC_DATACNTL = 65H ;寄存器写入值低字节
REC_BYTENUM = 66H ;CRC校验高字节
EEPROM_BUF = 67H ;CRC校验低字节
REC_BUFEND = 80H ;接收缓冲区尾部地址
ENDS
; ERROR CODE TO RETURN
F_ILLEGALFUNCTION EQU 01H
F_ILLEGALDATAADDR EQU 02H
F_ILLEGALDATAVAL EQU 03H
F_SALVEDEVICE EQU 04H
F_ACKNOWLEDGE EQU 05H
F_SLAVEDEVICEBUSY EQU 06H
F_NEGACKNOWLEDGE EQU 07H
F_MEMORYPARITY EQU 08H
ORG 0000H
AJMP PROGRAMSTART
DB 85H
EXTINT0_ISRP:
AJMP EXTINT0_ISR
LJMP IC_CARD_SUBP ; 0005H
LJMP RETURN_RESULT ; 0008H
TIMER0_ISRP:
AJMP TIMER0_ISR
T_AT_KEY:
DB 16H,1EH,26H, 5, 6, 4 ; 1 2 3 F1 F2 F3
EXTINT1_ISRP:
AJMP EXTINT1_ISR
DB 25H,2EH,36H,0CH, 3,0BH ; 4 5 6 F4 F5 F6
TIMER1_ISRP:
AJMP TIMER1_ISR
DB 3DH,3EH,46H,76H,75H,74H ; 7 8 9 ESC ^ >
SERIAL_ISRP:
AJMP SERIAL_ISR
DB 14H,45H,5AH,66H,6BH,72H ; R 0 <_| <- < V
TIMER2_ISRP:
AJMP TIMER2_ISR
CRC_KEYWORD:
DB 0A0H,01H,0
AUCHCRCHI: ;CRC 高字节值表
DB 000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB 001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB 001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB 000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB 001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB 000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB 000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB 001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB 001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB 000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB 000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB 001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB 000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
DB 001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB 001H,0C0H,080H,041H,000H,0C1H,081H,040H,000H,0C1H,081H,040H,001H,0C0H,080H,041H
DB 000H,0C1H,081H,040H,001H,0C0H,080H,041H,001H,0C0H,080H,041H,000H,0C1H,081H,040H
AUCHCRCLO: ;CRC低字节值表
DB 000H,0C0H,0C1H,001H,0C3H,003H,002H,0C2H,0C6H,006H,007H,0C7H,005H,0C5H,0C4H,004H
DB 0CCH,00CH,00DH,0CDH,00FH,0CFH,0CEH,00EH,00AH,0CAH,0CBH,00BH,0C9H,009H,008H,0C8H
DB 0D8H,018H,019H,0D9H,01BH,0DBH,0DAH,01AH,01EH,0DEH,0DFH,01FH,0DDH,01DH,01CH,0DCH
DB 014H,0D4H,0D5H,015H,0D7H,017H,016H,0D6H,0D2H,012H,013H,0D3H,011H,0D1H,0D0H,010H
DB 0F0H,030H,031H,0F1H,033H,0F3H,0F2H,032H,036H,0F6H,0F7H,037H,0F5H,035H,034H,0F4H
DB 03CH,0FCH,0FDH,03DH,0FFH,03FH,03EH,0FEH,0FAH,03AH,03BH,0FBH,039H,0F9H,0F8H,038H
DB 028H,0E8H,0E9H,029H,0EBH,02BH,02AH,0EAH,0EEH,02EH,02FH,0EFH,02DH,0EDH,0ECH,02CH
DB 0E4H,024H,025H,0E5H,027H,0E7H,0E6H,026H,022H,0E2H,0E3H,023H,0E1H,021H,020H,0E0H
DB 0A0H,060H,061H,0A1H,063H,0A3H,0A2H,062H,066H,0A6H,0A7H,067H,0A5H,065H,064H,0A4H
DB 06CH,0ACH,0ADH,06DH,0AFH,06FH,06EH,0AEH,0AAH,06AH,06BH,0ABH,069H,0A9H,0A8H,068H
DB 078H,0B8H,0B9H,079H,0BBH,07BH,07AH,0BAH,0BEH,07EH,07FH,0BFH,07DH,0BDH,0BCH,07CH
DB 0B4H,074H,075H,0B5H,077H,0B7H,0B6H,076H,072H,0B2H,0B3H,073H,0B1H,071H,070H,0B0H
DB 050H,090H,091H,051H,093H,053H,052H,092H,096H,056H,057H,097H,055H,095H,094H,054H
DB 09CH,05CH,05DH,09DH,05FH,09FH,09EH,05EH,05AH,09AH,09BH,05BH,099H,059H,058H,098H
DB 088H,048H,049H,089H,04BH,08BH,08AH,04AH,04EH,08EH,08FH,04FH,08DH,04DH,04CH,08CH
DB 044H,084H,085H,045H,087H,047H,046H,086H,082H,042H,043H,083H,041H,081H,080H,040H
; R0=STR PTR, R2=J=LEN(CRC BYTES INCLUDED), R3=I,R6=CRCHI,R7=CRCLO
MODBUS_CRC_EEPROM_BUF:
MOV R0,#EEPROM_BUF
MOV R2,#16
SJMP MODBUS_CRC
MODBUS_CRC_REC: ;CRC校验
MOV R0,#REC_STATIONADDR
MODBUS_CRC:
MOV R6,#0FFH
MOV R7,#0FFH
DEC R2
DEC R2
MODBUS_CRC_L:
MOV A,@R0
INC R0
XRL A,R6
MOV R3,A
MOV DPTR,#AUCHCRCHI
MOVC A,@A+DPTR
XRL A,R7
MOV R6,A
MOV A,R3
MOV DPTR,#AUCHCRCLO
MOVC A,@A+DPTR
MOV R7,A
DJNZ R2,MODBUS_CRC_L
MOV A,R6
XCH A,@R0
XRL A,@R0
MOV R3,A
INC R0
MOV A,R7
XCH A,@R0
XRL A,@R0
ORL A,R3
INC R0
RET
;**************************************************
EXTINT0_ISR: ;外部中断0
RETI
TIMER0_ISR: ;定时器T0中断
PUSH PSW
PUSH ACC
MOV PSW,#8
CLR EA ;关闭所有中断(很重要)中断禁区
CLR TR0 ;STOP TIMER0(很重要)硬件计数禁区
MOV A,#6DH ;11.0592 MHZ
ADD A,TL0 ;1 MS,从停止定时器TR0到重置定时器TR0有7条指令
MOV TL0,A ;为了完成这个,需要把计数器初值加7,因此0FC66H+7=0FC6DH
MOV A,#0FCH
ADDC A,TH0 ;方式1计数到达时不会自动赋初值,到00继续增加,所以需要把这些考虑进来
MOV TH0,A ;把从溢出到停止定时器以及这几条指令的执行时间全部考虑进来
SETB TR0 ;重新设置1ms的计数初值
SETB EA ;开中断
MOV A,R3 ; MS_COUNTER CLOCK
ADD A,#1
MOV R3,A
MOV A,R2 ; P1S_COUNTER
ADDC A,#0
MOV R2,A
MOV A,R6 ; 每定时时间1ms到,R6-1
JZ TIMER0_ISR_E ;一定要先判断是否为0,为0就说明没有接收到数据了
DJNZ R6,TIMER0_ISR_E ;减一后不为0,返回
SETB HAVEDATAINBUF ;减为0的时候说明前后两次时间间隔为4ms,一帧结束,设置标志位,缓冲区有数据
INC R7 ; F_NAP
TIMER0_ISR_E:
POP ACC
POP PSW ; FIELD RESTORE
RETI
EXTINT1_ISR: ;外部中断1
RETI
TIMER1_ISR: ;定时器1
RETI
;*********************串口中断接收********************
SERIAL_ISR:
PUSH PSW ; PSW和ACC保护
PUSH ACC
MOV PSW,#10H ; 使用寄存器2区
JNB RI,SERIAL_ISR_TX ;判断接收中断标志位是否有效
CLR RI ;有效,清除后继续
JB INTRANSMIT,SERIAL_ISR_TX ;由于半双工通信,所以MODBUS发送状态有效则跳转
JB HAVEDATAINBUF,SERIAL_ISR_TX ;前面数据包还没有取走则跳转
MOV INTERVALT,#4 ;使用R6的直接地址启动4ms定时,注意现在寄存器区已经换了不能直接R6
CJNE R0,#REC_BUFEND,SERIAL_ISR_RX1 ;R0代表接收缓冲区指针,REC是缓冲区尾部,要是相等则溢出,不相等转移继续接收
SJMP SERIAL_ISR_TX
SERIAL_ISR_RX1:
MOV A,SBUF ;读接收数据,并在PSW中产生接收数据的奇偶值
MOV @R0,A ;存接收数据
MOV C,P
MOV ACC.2,C ; SCON.2 = RB8
XRL A,SCON ; P和RB8是否相等
JB ACC.2,SERIAL_ISR_TX ;如果异或后第二位为1则出错,指针不变
INC R0 ;正确,指针加1
;*********************串口中断发送*****************
SERIAL_ISR_TX:
JNB TI,SERIAL_ISR_E ;判断发送中断标志位是否有效
CLR TI ;如果上次没发送完成,那么TI为0,发送完成后TI会被置1
JNB INTRANSMIT,SERIAL_ISR_E ;不是发送状态则结束
MOV A,R3 ; TRSBYTECNT发送字节数
JNZ SERIAL_ISR_TX1 ; 如果不为0则继续发送
CLR INTRANSMIT ;为0则已经发完,清除发送状态
CLR TRANSMITCTRL
CLR RECEIVECTRL ;清除P3.6,P3.7,则D19处于接收状态
MOV REC_DPOINTER,#REC_STATIONADDR ;接收指针指向接收缓冲区第一个字节
SJMP SERIAL_ISR_E ;中断返回
SERIAL_ISR_TX1:
MOV A,@R1
MOV C,P
MOV TB8,C
MOV SBUF,A ; TRS_DPOINTER启动发送
INC R1 ;发送指针加1
DEC R3 ;发送字节数减1
SERIAL_ISR_E:
POP ACC
POP PSW ; FIELD RESTORE
RETI
TIMER2_ISR:
RETI
;*************程序开始(初始化代码)**********************************
PROGRAMSTART: ;:HDW RESET
CLR EA ;关中断
CLR A
MOV PSW,A ;状态寄存器清零
MOV CTRLWRD,A ;28H清零
MOV CTRLWRD+1,A
CPL A
MOV P0,A
MOV P1,A
MOV P2,A
MOV P3,A ;所有输出锁存器全部置1
CLR TRANSMITCTRL ;P3.6=0 写清零
CLR RECEIVECTRL ;P3.7=0 读清零
MOV R5,#20 ;延时2s 可以由20变为2,即为0.2s
PROGRAMSTART_0:
MOV R6,#200 ; DELAY FOR 100 MILLISECONDS
PROGRAMSTART_1:
MOV R7,#230 ;两指令周期
DJNZ R7,$ ;两指令周期 500US
DJNZ R6,PROGRAMSTART_1 ; 200*0.5MS
DJNZ R5,PROGRAMSTART_0 ; 20*0.1S
CLR A
MOV R0,A
PROGRAMSTART_2:
INC R0
MOV @R0,A ; 0 ==> 0 -- FFH ;00-7FH,内部128RAM清零
CJNE R0,#80H-1,PROGRAMSTART_2 ; CLEAR DATA END
MOV SP,#TOPOFSTACK ;TOPOFSTACKC = 4FH
MOV TMOD,#21H ;T0设置为方式1,16位定时器;T1设置位方式2,8位定时器,初值自动重装
MOV TCON,#55H ;T1和T0开始计数,INT0和INT1外中断信号设置位下降沿有效
MOV TH0,#0FCH ; T0为1ms定时,3-4ms为帧间隔,通过这个判断帧起始和停止位置,以接受完整数据帧
MOV TL0,#66H ;计数初值为1111 1100 0110 0110 B = 2^16-1ms*11.059M /12 = 64614
MOV TH1,#-3 ; T1 3.255 μs ,T1为串行口提供溢出中断,
; T1初值= 256 - 11059200/(32*12*9600)= 253 = 0FDH (串行口波特率为9600b/s)
MOV SCON,#11010000B ;SM1=SM0=1 MODE 3, 9 BITS, 波特率由T1时间常数指定,SM2=0,接受所有数据并触发中断,
;REN=1,允许接收,低四位先全部清零
MOV IP,#00010000B ;设置串行口中断为高优先级中断
MOV IE,#10010010B ;EA=1,串行口中断允许ES=1,计时器T0中断允许ET0=1
;为了避免因T1溢出引起中断,此时应禁止T1中断
READSTATIONADDR:
MOV N_STATIONADDR,#1 ;定义从机地址1
;**************************主循环****************************************
WAITCOMMAND:
ACALL SETPGADDR ;28H,29H位处理给P1,P2端口
CLR HAVEDATAINBUF ;清除数据接收标志位
MOV REC_DPOINTER,#REC_STATIONADDR ;接收指针指向MODBUS接收区地址60H
MAIN_LOOP:
JNB HAVEDATAINBUF,MAIN_LOOP ;没有接收到数据,标志位为0
CLR HAVEDATAINBUF ;接收到数据,标志位清零后继续
MOV A,REC_STATIONADDR ;把接收区的第一个字节内容给A
; NOT ACK BROADCAST
CJNE A,N_STATIONADDR,WAITCOMMAND ;判断第一个字节是否位从机地址1,如果是1才是发给自己的
MOV R2,#8 ;利用R2判断帧长是不是错误,一个数据包8个字节
MOV A,REC_COMMAND
CJNE A,#16,MAIN_LOOP2 ;功能码为16对应情况 16号与3,6帧长不同
MOV A,REC_BYTENUM
ADD A,#9
MOV R2,A ;如果是16号,帧字节加9后再存入R2
MAIN_LOOP2:
MOV A,REC_DPOINTER
ADD A,#-REC_STATIONADDR
CJNE A,R2,WAITCOMMAND ;末-头=R2?
ACALL MODBUS_CRC_REC ;判断CRC有没有错误
JNZ WAITCOMMAND ;出错,重新接收
MOV A,REC_COMMAND
CJNE A,#3,$+5
AJMP CMDM3
CJNE A,#6,$+5 ;跳转到功能码6对应程序
AJMP CMDM6
CJNE A,#16,$+5
AJMP CMDM16
ERRCOMMAND:
MOV A,#F_ILLEGALFUNCTION
AJMP EXCEPTRET
EXCEPTRET:
MOV EXCEPTIOCODE,A
ORL REC_COMMAND,#80H
MOV R2,#5
;AJMP RETURN_RESULT
RETURN_RESULT: ;进行返回
MOV A,TRS_STATIONADDR ;返回60H开始的内容
JZ WAITCOMMAND
MOV TRSBYTECNT,R2 ;发送字节长度
ACALL MODBUS_CRC_REC ;CRC校验
SETB INTRANSMIT ;半双工标志位置发送
SETB TRANSMITCTRL
SETB RECEIVECTRL ;D19设为发送状态
MOV TRS_DPOINTER,#TRS_STATIONADDR ;设置发送首地址
CLR A
MOV INTERVALT,A ;定时时间清零
SETB TI ;发送完成TI置1,下次就能发送了
AJMP WAITCOMMAND
;*******************功能码3***************************
CMDM3:
MOV A,REC_DATAADDRH
ORL A,REC_DATACNTH
JNZ ERRDATAADDR ;相或不为0报错
MOV A,REC_DATAADDRL
ADD A,REC_DATACNTL
JC ERRDATAADDR ;相加有进位报错
ADD A,#-20 ; MAX DATA ADDR = 19
JC ERRDATAADDR
MOV A,REC_DATACNTL
JZ ERRDATAVAL ;寄存器数量低字节为0报错
ADD A,#-14 ; MAX RETURN DATA LEN 14 = 26/2+1
JC ERRDATAVAL
MOV A,REC_DATACNTL ;提取待读取的寄存器数量
ADD A,ACC
MOV RETURNBYTECNT,A ;RETURNBYTECNT=2*REC_DATACNTL,读取数据的字节数,为寄存器数*2
MOV R2,A
MOV A,REC_DATAADDRL ;对寄存器操作
ADD A,ACC
ADD A,#PARASTARTADDR
MOV R0,A ;R0提取待读取的寄存器地址
MOV R1,#REC_DATAADDRL ;R1提取寄存器地址
CMDM3A:
MOV A,@R0 ;把R0指向寄存器内容读出来
MOV @R1,A ;放在寄存器R1指向寄存器中
INC R0
INC R1
DJNZ R2,CMDM3A ;读取字节数量
MOV A,RETURNBYTECNT
ADD A,#5 ;加上从机号,功能码,字节数,CRC高低字节
MOV R2,A
AJMP RETURN_RESULT
ERRDATAADDR: ;出错返回
MOV A,#F_ILLEGALDATAADDR
AJMP EXCEPTRET
ERRDATAVAL:
MOV A,#F_ILLEGALDATAVAL
AJMP EXCEPTRET
;******************功能码6************************
CMDM6:
MOV A,REC_DATAADDRH ;寄存器地址高字节应该为0
JNZ ERRDATAADDR ;不为零转移
MOV A,REC_DATAADDRL ;寄存器地址低字节
ADD A,#-20 ; MAX DATA ADDR = 20
JC ERRDATAADDR ;有进位说明出错,转移,如果要求改第21个寄存器就会出错
MOV A,REC_DATAADDRL
ADD A,ACC ;对需要操作的寄存器处理
ADD A,#PARASTARTADDR ;写入地址0X28H
MOV R0,A ;R0:寄存器地址:PARASTARTADDR+REC_DATAADDRL
MOV A,REC_DATACNTH ;寄存器写入值高字节,放在0X28H
MOV @R0,A ;这里使用的是寄存器间接寻址,因为这里不止一个寄存器
INC R0
MOV A,REC_DATACNTL ;寄存器写入值低字节,放在0X29H
MOV @R0,A ;这里28H,29H是一号寄存器,2AH,2BH是二号,一次类推
MOV R2,#8 ;返回8个字节,这里省略了返回内容,因为6号功能发送和返回内容一致,只是告诉要返回8字节
AJMP RETURN_RESULT
;******************功能码16***********************
CMDM16:
MOV A,REC_DATAADDRH
ORL A,REC_DATACNTH
JNZ ERRDATAADDR
MOV A,REC_DATAADDRL
ADD A,REC_DATACNTL
JC ERRDATAADDR
ADD A,#-20 ; MAX DATA ADDR = 20
JC ERRDATAADDR
MOV A,REC_DATACNTL
JZ ERRDATAVAL ; 寄存器数量低字节不能为0
MOV R2,REC_BYTENUM ; 字节数
MOV A,REC_DATAADDRL
ADD A,ACC
ADD A,#PARASTARTADDR
MOV R1,A ; 待预置寄存器的地址
MOV R0,#REC_BYTENUM+1 ; 寄存器写入字节地址
CMDM6A:
MOV A,@R0
MOV @R1,A
INC R0
INC R1
DJNZ R2,CMDM6A ;写完所有字节
MOV R2,#8 ;返回8个字节
AJMP RETURN_RESULT
IC_CARD_SUBP:
AJMP WAITCOMMAND
;************************
;28H和29H通用数据存储器这两个端口进行位变换以后送到P1端口和P2端口,
;也就是MODBUS的寄存器0在51单片机中对应的是28H和29H这两个字节构成的16位的字,
;注意到28H和29H存在于可以位寻址的空间,实际上是用这两个构成了16位寄存器。
;单片机启动后接收到两个字节的数据,然后通过位变换到P1.0-P1.7,P2.0-P2.5,进行14个选择。
;************************
SETPGADDR: ;0x28H,0x29H送到P2,P1,一位一位传
MOV A,CTRLWRD+1 ;CTRLWRD+1=29H
MOV C,ACC.0
MOV B_S2,C ;P2.0
MOV C,ACC.1
MOV B_S3,C ;P2.1
MOV C,ACC.2
MOV B_S4,C ;P2.2
MOV C,ACC.3
MOV B_S5,C ;P2.3
MOV C,ACC.4
MOV B_S6,C ;P2.4
MOV C,ACC.5
MOV B_S7,C ;P2.5
MOV C,ACC.6
MOV B_S8,C ;P1.5
MOV C,ACC.7
MOV B_S9,C ;P1.7
MOV A,CTRLWRD ;CTRLWRD=28H
MOV C,ACC.0
MOV B_S10,C ;P1.6
MOV C,ACC.1
MOV B_S11,C ;P1.4
MOV C,ACC.2
MOV B_S12,C ;P1.3
MOV C,ACC.3
MOV B_S13,C ;P1.2
MOV C,ACC.4
MOV B_S14,C ;P1.1
MOV C,ACC.5
MOV B_S15,C ;P1.0
RET
DB 'V1.1 2003.02.20'
END