?? os51asm.asm
字號:
;********************************************************************************************************
; uC/OS
; The Real-Time Kernel
;
; 52 SPECIFIC ASSEMBLY LANGUAGE CODE
;
; Rene Voorberg
; 80c52 (Large Model)
; Using the COMPASS51 compiler from PLC Texas USA
;
; File : OS51asm.asm
;********************************************************************************************************
;Conventions:
;opt :optimized code but is shows you what's happening
;P3.5 :break bit which is used by the Intel Rism Drivers or ROM monitors from
;PLC to signal a breakpoint. If we are stepping or a breakpoint is hit
;no interrupts should be honoured no more beside the serial interrupt for
;your debugger.
;_sp :the (integer) xstackpointer used by the Compass 51 compiler
;_fp :the (integer) framepointer used by the Compass 51 compiler
;OSTCBCur:
; Pointer to the current's process OSTCB
;OSTCBHighRdy:
; Pointer to the process OSTCB with the highest priority which is ready to run
XDEF _OSStartHighRdy
XDEF _OSCtxSw
XDEF _OSIntCtxSw
XDEF _OSTickIsr
;I forced OSTCBHighRdy to be near in ucos51.c
XREF _OSTCBHighRdy
;I forced OSTCBCur to be near in ucos51.c
XREF _OSTCBCur
XREF _OSIntExit
XREF _OSIntEnter
XREF _OSTimeTick
XREF _tick
XREF __fp:DATA
XREF __sp:DATA
;Extra divisor for slow tick rates
XREF _OSTick:DATA
;I forced OSIntNesting to be near in ucos51.c
XREF _OSIntNesting:DATA
XREF _OSTCBCur:DATA
XREF _OSTCBHighRdy:DATA
XDEF _RestoreState
;To have the two task pointers run fast we store them near
;The code can also be used with far OSTCBCur and HighRdy
;then you must remove the next define.
near EQU 1
;include the appropriate files
INCLUDE "80C320.INC"
;MCS51_CRYSTAL_FREQ equ 18432000
;OS_TICKS_PER_SEC equ 20 possible with 80c320 or 80c151
MCS51_CRYSTAL_FREQ equ 11059200
OS_TICKS_PER_SEC equ 10
;Determine the reload values for timer 1 this is automated by the next macro
TIMER0RELOAD equ (MCS51_CRYSTAL_FREQ / (OS_TICKS_PER_SEC * 12))
T0RELOADM equ (TIMER0RELOAD>>16)+1 ; +1 decause of the djnz instruction which should at least run once
T0RELOADH equ (65536-(TIMER0RELOAD&65535))>>8
T0RELOADL equ (65536-(TIMER0RELOAD&65535))&255
;Install the interrupt vector for your timer tick in your main program
;In compass we can do it here like:
; LVECTOR 00Bh , _OSTickIsr
;The following macro is the assembly code to save the current task's state
;SEGMENT c_text
save_context: MACRO
PUSH PSW
PUSH ACC
PUSH B
PUSH 0
PUSH 1
PUSH 2
PUSH 3
PUSH 4
PUSH 5
PUSH 6
PUSH 7
PUSH DPH
PUSH DPL
PUSH __fp ;save the framepointer (Compass 51)
PUSH __fp+1
PUSH __sp ;save the stackpointer
PUSH __sp+1
CLR EA
;copy internal stack to external
CLR C
MOV A,SP
;We intialized every tasks internal stackpointer to 80h
;in order to determine howe far(near) it has grown we need
;to substract 7Fh from the stackpointer
SUBB A,#7Fh
MOV R1,A ;istacklength in R1
MOV R5,A ;also save to move it on top of xstack
MOV R0,#80h ;Start address of istack pointer
MOV DPH,__sp ;Get the xstack pointer
MOV DPL,__sp+1
INC DPTR ;point to the next free location because
;the xstack pointer points to the last pushed value
$$save_ctx: ;Copy the istack to xstack
;(the $$ in the macro is for repetitive macro call labels)
;compiler generates $1save_ctx first time round and $2 2nd time
MOV A,@R0 ;Get the byte from the istack
MOVX @DPTR,A ;move it into xstack
INC DPTR ;advance
INC R0 ;advance
DJNZ R1,$$save_ctx ;Copy the whole internal stack into external stack
;and adjust _sp
MOV A,R5 ;Save the istack length onto the xstack
MOVX @DPTR,a ;
INC DPTR ;__sp should point to the next free byte
MOV __sp,DPH ;Save the new stack pointer to __sp
MOV __sp+1,DPL
MOV SP,#80h ;Start the new task with SP set to #80h
ENDMAC save_context
restore_context: MACRO interrupt
CLR EA
IFMA 1
;execute this macro with 1 if we have
;__sp already filled with the appropriate xstack location
;seems difficult but for speedreasons it saves some states.
; this is used in for example an interrupt
;We need to copy __sp in dptr
; DPTR=_SP
MOV DPH,__sp
MOV A,__sp+1
ELSE
;enter this macro with dptr loaded with OSTCBCur:
;the new task. We need to copy OSTCBCur->StkPtr in dptr
; DPTR=_SP=@DPTR
MOVX A,@DPTR
MOV R3,A
INC DPTR
MOVX A,@DPTR
;opt MOV DPL,A
MOV DPH,R3 ;dptr now points to the last free location
ENDIF
; DPTR-=1
CLR C
;opt MOV A,DPL
SUBB A,#1h ;decrement dptr because it is pointing to the next free xstack position
MOV DPL,A
JNC $$rest_ctx2
DEC DPH ;DPTR now points to the end of the tasks stack
$$rest_ctx2:
; A=@DPTR
MOVX A,@DPTR ;first byte is number of bytes on external stack
; DEC A ;correct that we have read the nr of stacked values
MOV R5,A
CLR C
MOV A,DPL
SUBB A,R5 ;substract it from the DPTR
MOV DPL,A
JNC $$rest_ctx3
DEC DPH ;DPTR now points to the base of the tasks stack
$$rest_ctx3:
MOV R0,#80H ;Every task has its internal stack at IRAM
MOV 1,5 ;Copy nr of bytes on stack in r1
$$rest_ctx4:
MOVX A,@DPTR
MOV @R0,A
INC DPTR
INC R0
DJNZ R1,$$rest_ctx4 ;Copy the whole stack back into internal stack
MOV A,R5 ;Copy nr of bytes on stack in A
ADD A,#7Fh ;Add 127 for offset for stackpointer not 128
;because SP points to a byte that contains data
;so 1 byte on stack should be in 80h
MOV SP,A ;Restore the stackpointer
SETB EA
LJMP _RestoreState
ENDMAC restore_context
;i use this 'function' to return form every interrupt or ctx switch
;it saves space. Interrupts can not enter by a shared routine because the
;stack can't be fixed to jump to the particular interrupt handler
_RestoreState:
POP __sp+1 ;Start restoring the context
POP __sp
POP __fp+1
POP __fp
POP DPL
POP DPH
POP 7
POP 6
POP 5
POP 4
POP 3
POP 2
POP 1
POP 0
POP B
POP ACC
POP PSW
RETI
;*********************************************************************************************************
; START MULTITASKING
; void OSStartHighRdy(void)
;
; Total execution time : xxbus cycles
;*********************************************************************************************************
_OSStartHighRdy:
CLR EA
IFDEF near
; _OSTCBCur=_OSTCBHighRdy
MOV _OSTCBCur,_OSTCBHighRdy
MOV _OSTCBCur+1,_OSTCBHighRdy+1
MOV DPL,_OSTCBCur+1 ;dptr point TO OSTCBCur->STCKPTR
MOV DPH,_OSTCBCur
ELSE
MOV DPTR,#(_OSTCBHighRdy) ;R6:R7 = OSTCBHighRdy
MOVX A,@DPTR
MOV R6,A
INC DPTR
MOVX A,@DPTR
MOV R7,A
MOV DPTR,#(_OSTCBCur) ;OSTCBCur = OSTCBHighRdy
MOV A,R6
MOVX @DPTR,A
INC DPTR
MOV A,R7
MOVX @DPTR,A
MOV DPL,R7 ;point TO OSTCBCUR->STCKPTR
MOV DPH,R6
ENDIF
restore_context
;*********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From task level)
; void OSCtxSw(void)
;
; Total execution time : xx bus cycles
;*********************************************************************************************************
_OSCtxSw:
save_context
IFDEF near
; DPTR=OSTCBCur
MOV DPH,_OSTCBCur ;dptr point TO OSTCBCur->STCKPTR
MOV DPL,_OSTCBCur+1
ELSE
; DPTR=OSTCBCur
MOV DPTR,#(_OSTCBCur) ;R6:R7 = OSTCBCur
; WR6=@DPTR
MOVX A,@DPTR
MOV R6,A
INC DPTR
MOVX A,@DPTR
;opt MOV R7,A
; DPTR=WR6
;opt MOV DPL,R7 ;point TO OSTCNCUR->STCKPTR
MOV DPL,A ;point TO OSTCNCUR->STCKPTR
MOV DPH,R6
ENDIF
; @DPTR=_SP
MOV A,__sp
MOVX @DPTR,A
INC DPTR
MOV A,__sp+1
MOVX @DPTR,A
IFDEF near
; OSTCBCur=OSTCBHighRdy
MOV _OSTCBCur,_OSTCBHighRdy
MOV _OSTCBCur+1,_OSTCBHighRdy+1
MOV DPL,_OSTCBCur+1 ;dptr point TO OSTCBCur->STCKPTR
MOV DPH,_OSTCBCur
ELSE
; DPTR=OSTCBHighRdy
MOV DPTR,#(_OSTCBHighRdy) ;get OSTCBHighRdy
; WR4=@DPTR
MOVX A,@DPTR
MOV R4,A
INC DPTR
MOVX A,@DPTR
MOV R5,A
; DPTR=OSTCBCur
MOV DPTR,#(_OSTCBCur) ;get OSTCBCur
; @DPTR=WR4
MOV A,R4
MOVX @DPTR,A
INC DPTR
MOV A,R5
MOVX @DPTR,A
; DPTR=WR4
MOV DPL,R5 ;point TO OSTCNHighRdy->STCKPTR
MOV DPH,R4
ENDIF
restore_context
_OSIntCtxSw: ;First repair the stackpointer for two calls
CLR EA ;Do not interrupt during this sequence
CLR C ;copy internal stack to external
MOV A,SP
ADD A,#-4 ;correct 4 bytes on stack for two calls (four bytes)
MOV SP,A ;Save the stackpointer
;On the stack the first two bytes are _sp
;we need to refresh it because it might have been corrupted
;in the interrupt routine
;We pop the _sp into DP because we need it to copy the
;internal stack to
POP DPL ;
POP DPH ;Get the external stack pointer
PUSH DPH ;Save it again to keep the stack intact
PUSH DPL ;
; POP __sp+1 ;Optimised
; POP __sp ;Start testoring the context
; PUSH __sp ;
; PUSH __sp+1 ;
;Now copy the internal stack to the external stack
; CLR EA ;Do not interrupt during this sequence
;copy internal stack to external
CLR C
; MOV A,SP ;Get the stackpointer ;Optimised SP is already in ACC
SUBB A,#7Fh ;See how many bytes are on stack
MOV R1,A ;Save in R1
MOV R5,A ;Save to move on top of xstack
MOV R0,#80h ;Bottom of internal stack
;Optimised ;The next section if performed by the POP and PUSH of DP in the previous lines
; MOV DPH,__sp ;Get the external stack pointer
; MOV DPL,__sp+1
INC DPTR ;Point to the next free location
$$save_ctx:
MOV A,@R0
MOVX @DPTR,A
INC DPTR
INC R0
DJNZ R1,$$save_ctx ;Copy the whole internal stack into external stack
;and adjust _sp
MOV A,R5
MOVX @DPTR,a
; __sp should point to the next free byte
INC DPTR
MOV __sp,DPH
MOV __sp+1,DPL
; Optimised MOV SP,#80h
IFDEF near
; DPTR=OSTCBCur
MOV DPH,_OSTCBCur ;dptr point TO OSTCBCur->STCKPTR
MOV DPL,_OSTCBCur+1
ELSE
; DPTR=OSTCBCur
MOV DPTR,#(_OSTCBCur) ; 4~, R6:R7 = OSTCBCur
; WR6=@DPTR
MOVX A,@DPTR
MOV R6,A
INC DPTR
MOVX A,@DPTR
;opt MOV R7,A
; DPTR=WR6
;opt MOV DPL,R7 ;point TO OSTCNCUR->STCKPTR
MOV DPL,A ;point TO OSTCNCUR->STCKPTR
MOV DPH,R6
ENDIF
; @DPTR=_SP
MOV A,__sp
MOVX @DPTR,A
INC DPTR
MOV A,__sp+1
MOVX @DPTR,A
IFDEF near
; OSTCBCur=OSTCBHighRdy
MOV _OSTCBCur,_OSTCBHighRdy
MOV _OSTCBCur+1,_OSTCBHighRdy+1
MOV DPL,_OSTCBCur+1 ;dptr point TO OSTCBCur->STCKPTR
MOV DPH,_OSTCBCur
ELSE
; DPTR=OSTCBHighRdy
MOV DPTR,#(_OSTCBHighRdy) ; 4~, R6:R7 = OSTCBHighRdy
; WR4=@DPTR
MOVX A,@DPTR
MOV R4,A
INC DPTR
MOVX A,@DPTR
MOV R5,A
; DPTR=OSTCBCur
MOV DPTR,#(_OSTCBCur) ; 4~, R6:R7 = OSTCBCur
; @DPTR=WR4
MOV A,R4
MOVX @DPTR,A
INC DPTR
MOV A,R5
MOVX @DPTR,A
; DPTR=WR4
MOV DPL,R5 ;point TO OSTCBHighRdy->STCKPTR
MOV DPH,R4
ENDIF
restore_context ;Fix the stack nd return
_OSTickIsr:
JNB P3.5,OSTick2 ;if break flag set
OSTick1:
RETI
OSTick2:
DJNZ _OSTick,OSTick1
MOV _OSTick,#T0RELOADM ;hsb value from macro
MOV TL0,#T0RELOADL ;lsb value set by user
MOV TH0,#T0RELOADH ;m(id)sb value set by user
OSTick3:
CPL P1.7 ;toggle bit for scope or something
PUSH PSW
PUSH ACC
PUSH B
PUSH 0
PUSH 1
PUSH 2
PUSH 3
PUSH 4
PUSH 5
PUSH 6
PUSH 7
PUSH DPH
PUSH DPL
PUSH __fp
PUSH __fp+1
PUSH __sp
PUSH __sp+1
; LCALL _OSIntEnter
INC _OSIntNesting ;Read modify write increment of OSIntNesting
; SETB EA
LCALL _OSTimeTick
;The next instruction shoul only be performed if you get it out of the
;ucos code. Remove OSIntNesting--; from OSIntExit
;This way is faster than the code generated by any compiler
DEC _OSIntNesting ;Read modify write increment of OSIntNesting
LCALL _OSIntExit ;Note ! increment of OSIntNesting removed from the UCOS code
LJMP _RestoreState ;Use the shared code to exit the interrupt handler
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -