wingman/PPMDecoder/ASM/RCRXDecoderV2.asm

658 lines
24 KiB
NASM
Executable File

;==========================================================================================================
;
; CMtec RCRXDecoder copyright 2005 v1.0 (2005-01-23)
;
; Name: Christoffer Martinsson
; E-mail: cm@wsm.se
;
; 7ch PPM decoder for RC-Receivers.
; Based on Atmel AT90S2313 or ATTiny2313 with 10 or 20Mhz crystal.
;
; PORTB,PB0-PB7 assign to output ch 1 to ch 7 (ch 8 reserved)
; PORTD,PD2 assign to input PPM-signal from RX
; PORTD,PD3 assign to StoreButton
; PORTD,PD4 assign to LED
;
; The inputsignal is decoded into two separate buffert locations in the SRAM. The output-routine work
; with the buffert that the input-routine NOT work with. After a "good" frame is decoded the buffertPointer
; is switch. This to ensure that a "good" pulseframe allways is available if a error should occur.
;
; Flowchart: RCRXDecoder.vsd
;
; * Up to 8 channels
; * 100step/ms resolution at 10Mhz, 200step/ms resolution at 20Mhz
; * Allways a "good" output-signal
; * Output is "Freezed" if error occurs
; * Failsafe sets predefined values if error not recovered in (recomended) 2sec (max 8sec/10Mhz, 4sec/20Mhz)
; * Failsafe-value easily changed by pressing a button
; * PulseValue filtered to a smooth average-value
; * Error/Glitch indicator
;
;==========================================================================================================
; Processor-type
.EQU AT90S2313 = 1
;EQU ATTiny2313 = 1
.NOLIST ; Disable listfile generation
.ifdef AT90S2313
.INCLUDE "2313def.inc"
.else
.INCLUDE "tn2313def.inc"
.endif
.LIST ; Reenable listfile generation
; General definitions
; Adjust this numbers to make it work propely with your application
.EQU XTAL =10 ; Used crystal (Mhz) (min 10Mhz)
.EQU RecoveryNr =1 ; Number of "good" pulseFrames to recover from error-state
.EQU NrOfChannels =7 ; Numbers of channels (max 8)
.EQU FailSafeTime =2 ; Time before enter failsafe (sec) (max 8sec/10Mhz, 4sec/20Mhz)
; 8bit register
.DEF temp =r16 ; Temporary register
.DEF nextSRAMAddress =r12 ; Register for storing secondary SRAMAddress (buffert2)
.DEF debounceFilter =r15 ; Button-debounce register
.DEF timeout =r17 ; Timeout counter
.DEF chAddressEE =r18 ; EEPROM channelAddress
.DEF chAddressIn =r19 ; Input channelAddress
.DEF chAddressOut =r20 ; Output channelAddress
.DEF chAddressFS =r21 ; Failsafe channelAddress
.DEF pulseError =r22 ; PulseErrorCounter
.DEF pulseFlag =r23 ; Flag register
; "16bit" register
.DEF frameCalcL =r13 ; Register for calculate FrameTime
.DEF frameCalcH =r14
.DEF tempL =r24 ; Temporary register
.DEF tempH =r25
.DEF pulseInL =r26 ; InputPulseCounter
.DEF pulseInH =r27
.DEF pulseOutL =r28 ; OutputPulseCounter
.DEF pulseOutH =r29
; pulseFlag bit-definitions
.EQU SYNC =0 ; Sync found-Flag
.EQU LEDredActive =1 ; LEDred Active-Flag
.EQU LEDyellowActive =2 ; LEDyellow Active-Flag
.EQU BuffertToUse =3 ; Buffert-Flag (0=buffert1, 1=buffert2)
; Output bit-definitions
.EQU PPMSignal =2 ; PD2 Input for PPM-signal
.EQU StoreButton =3 ; PD3 Input for StoreButton
.EQU LEDred =4 ; PD4 Output for LEDred
.EQU LEDyellow =5 ; PD4 Output for LEDyellow
; Memory definitions
.EQU SRAMaddress1 =0x0060 ; Startlocation for buffert1 in SRAM
.EQU SRAMAddress2 =0x0074 ; Startlocation for buffert2 in SRAM
.EQU EEPROMaddress =0x0000 ; Startlocation for failsafe-storage in EEPROM
; Time definitions (timer0)
.EQU FSTimeoutTime =(2*XTAL*FailSafeTime) ; 1sec*FailSafeTime
; Time definitions (timer1)
.EQU MinPulseTime =(95*(XTAL/10)) ; ~0,95ms
.EQU MaxPulseTime =(220*(XTAL/10)) ; ~2,2ms
.EQU MinSyncTime =(400*(XTAL/10)) ; ~4ms
.EQU MaxSyncTime =(1400*(XTAL/10)) ; ~14ms
.EQU FrameTime =(1950*(XTAL/10)) ; ~20ms
.CSEG ; CODE segment
.ORG 0
;==========================================================================================================
; Reset- and Interrupt-vectors
;==========================================================================================================
RJMP Main ; Reset Handler
RJMP Ex_Int0 ; External Interrupt0 Handler
RETI ; External Interrupt1 Handler
RETI ; Timer1 Capture Handler
RJMP Timer_Int1 ; Timer1 CompareA Handler
RETI ; Timer1 Overflow Handler
RJMP Timer_Int0 ; Timer0 Overflow Handler
RETI ; USART0 RX Complete Handler
RETI ; USART0,UDR Empty Handler
RETI ; USART0 TX Complete Handler
RETI ; Analog Comparator Handler
.ifdef ATTiny2313
RETI ; Pin Change Interrupt
RETI ; Timer1 Compare B Handler
RETI ; Timer0 Compare A Handler
RETI ; Timer0 Compare B Handler
RETI ; USI Start Handler
RETI ; USI Overflow Handler
RETI ; EEPROM Ready Handler
RETI ; Watchdog Overflow Handler
.endif
;==========================================================================================================
; Macro
;==========================================================================================================
;----------------------------------------------------------------------------------------------------------
; Load16Temp
;
; Description: Load 16bit tempReg with 16bit @0-value
;
; Input: @0,@1
; ChangedReg: tempH,tempL
; UsedReg: tempH,tempL
;----------------------------------------------------------------------------------------------------------
.MACRO Load16Temp
LDI tempH,HIGH(@0)
LDI tempL,LOW(@0)
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; Compare16Temp
;
; Description: Compare 16bit tempReg with 16bit @0,@1-reg
;
; Input: @0,@1
; ChangedReg: Carry
; UsedReg: tempH,tempL
;----------------------------------------------------------------------------------------------------------
.MACRO Compare16Temp
CP tempL,@1
CPC tempH,@0
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; Clear16Reg
;
; Description: Clear 16bit @0,@1-register
;
; Input: @0,@1
; ChangedReg: @0,@1
; UsedReg: -
;----------------------------------------------------------------------------------------------------------
.MACRO Clear16Reg
CLR @0
CLR @1
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; Add16Reg
;
; Description: Add 16bit @2,@3 to 16bit @0,@1
;
; Input: @0,@1
; ChangedReg: @0,@1,Carry
; UsedReg: -
;----------------------------------------------------------------------------------------------------------
.MACRO Add16Reg
ADD @1,@3
ADC @0,@2
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; Sub16Reg
;
; Description: Subtract 16bit @2,@3 from 16bit @0,@1
;
; Input: @0,@1
; ChangedReg: @0,@1,Carry
; UsedReg: -
;----------------------------------------------------------------------------------------------------------
.MACRO Sub16Reg
SUB @1,@3
SBC @0,@2
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; DivByTwo16Reg
;
; Description: Divide 16bit @0,@1 by two
;
; Input: @0,@1
; ChangedReg: @0,@1
; UsedReg: -
;----------------------------------------------------------------------------------------------------------
.MACRO DivByTwo16Reg
CLC
ROR @0
ROR @1
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; Write16SRAM
;
; Description: Write data in 16bit @1,@2-reg to SRAM at position @0
;
; (BuffertToUse-Flag: 0=buffert1, 1=buffert2)
;
; Input: @0,@1,@2
; ChangedReg: -
; UsedReg: ZL,pulseFlag
;----------------------------------------------------------------------------------------------------------
.MACRO Write16SRAM
LDI ZL,LOW(SRAMaddress1)
SBRC pulseFlag,BuffertToUse
ADD ZL,nextSRAMAddress
ADD ZL,@0
ST Z+,@1
ST Z,@2
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; Read16SRAM
;
; Description: Read data in SRAM at position @0 to 16bir @1,@2-reg
;
; (BuffertToUse-Flag: 0=buffert1, 1=buffert2)
;
; Input: @0,@1,@2
; ChangedReg: @1,@2
; UsedReg: ZL,pulseFlag
;----------------------------------------------------------------------------------------------------------
.MACRO Read16SRAM
LDI ZL,LOW(SRAMaddress1)
SBRC pulseFlag,BuffertToUse
ADD ZL,nextSRAMAddress
ADD ZL,@0
LD @1,Z+
LD @2,Z
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; Write16EEPROM
;
; Description: Write data in 16bit @1,@2-reg to EEPROM at position @0
;
; Input: @0,@1,@2
; ChangedReg: -
; UsedReg: temp,EECR,EEARL,EEDR
;----------------------------------------------------------------------------------------------------------
.MACRO Write16EEPROM
MWEEWait1: SBIC EECR,1
RJMP MWEEWait1
LDI temp,LOW(EEPROMaddress)
ADD temp,@0
OUT EEARL,temp
OUT EEDR,@1
SBI EECR,EEMWE
SBI EECR,EEWE
MWEEWait2: SBIC EECR,1
RJMP MWEEWait2
LDI temp,LOW(EEPROMaddress)
ADD temp,@0
INC temp
OUT EEARL,temp
OUT EEDR,@2
SBI EECR,EEMWE
SBI EECR,EEWE
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; Read16EEPROM
;
; Description: Read data in EEPROM at position @0 to 16bir @1,@2-reg
;
; Input: @0,@1,@2
; ChangedReg: @1,@2
; UsedReg: temp,EECR,EEARL,EEDR
;----------------------------------------------------------------------------------------------------------
.MACRO Read16EEPROM
MREEWait1: SBIC EECR,1
RJMP MREEWait1
LDI temp,LOW(EEPROMaddress)
ADD temp,@0
OUT EEARL,temp
SBI EECR,EERE
IN @1,EEDR
MREEWait2: SBIC EECR,1
RJMP MREEWait2
LDI temp,LOW(EEPROMaddress)
ADD temp,@0
INC temp
OUT EEARL,temp
SBI EECR,EERE
IN @2,EEDR
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; StartTimer0
;
; Description: Start timer0. Prescale set to CK/1024
;
; Input: -
; ChangedReg: -
; UsedReg: temp,TCCR0
;----------------------------------------------------------------------------------------------------------
.MACRO StartTimer0
LDI temp,0x05
OUT TCCR0,temp ; Prescale set to CK/1024
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; StopTimer0
;
; Description: Stop timer0
;
; Input: -
; ChangedReg: -
; UsedReg: temp,TCCR0
;----------------------------------------------------------------------------------------------------------
.MACRO StopTimer0
CLR temp
OUT TCCR0,temp
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; StartTimer1
;
; Description: Star timer1. Prescale set to CK
;
; Input: -
; ChangedReg: -
; UsedReg: temp,OCR1AH,OCR1AL,TCCR1B
;----------------------------------------------------------------------------------------------------------
.MACRO StartTimer1
LDI temp,HIGH(100)
OUT OCR1AH,temp
LDI temp,LOW(100)
OUT OCR1AL,temp
LDI temp,0x09
OUT TCCR1B,temp ; Prescale set to CK
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; StopTimer1
;
; Description: Stop timer1
;
; Input: -
; ChangedReg: -
; UsedReg: temp,TCCR1B
;----------------------------------------------------------------------------------------------------------
.MACRO StopTimer1
CLR temp
OUT TCCR1B,temp
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; StoreTempOnStack
;
; Description: Store Status and temp-register on Stack
;
; Input: -
; ChangedReg: -
; UsedReg: temp,tempH,tempL,SREG
;----------------------------------------------------------------------------------------------------------
.MACRO StoreTempOnStack
PUSH tempH
PUSH tempL
PUSH temp
IN temp,SREG
PUSH temp
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; RestoreTempFromStack
;
; Description: Restore Status and temp-register from Stack
;
; Input: -
; ChangedReg: temp,tempH,tempL,SREG
; UsedReg: temp,tempH,tempL,SREG
;----------------------------------------------------------------------------------------------------------
.MACRO RestoreTempFromStack
POP temp
OUT SREG,temp
POP temp
POP tempL
POP tempH
.ENDMACRO
;----------------------------------------------------------------------------------------------------------
; SwitchBuffert
;
; Description: Switches buffertPointer in SRAM (BuffertToUse-Flag: 0=buffert1, 1=buffert2)
;
; Input: -
; ChangedReg: pulseFlag
; UsedReg: temp,pulseFlag
;----------------------------------------------------------------------------------------------------------
.MACRO SwitchBuffert
LDI temp,(1<<BuffertToUse)
EOR pulseFlag,temp ; Switch Buffert in SRAM
.ENDMACRO
;==========================================================================================================
; Interrupt routines
;==========================================================================================================
;----------------------------------------------------------------------------------------------------------
; Ex_Int0
;
; Description: Measure incoming pulse-value, filter it and then store it in SRAM.
;----------------------------------------------------------------------------------------------------------
Ex_Int0: StoreTempOnStack
ChkSync: SBRC pulseFlag,SYNC ; Check if sync found
RJMP ChkError
Load16Temp MinSyncTime
Compare16Temp pulseInH,pulseInL ; Chrck if pulseIn > MinSyncTime
BRCC Error;
Load16Temp MaxSyncTime
Compare16Temp pulseInH,pulseInL ; Check if pulseIn < MaxSyncTime
BRCS Error
SBR pulseFlag,(1<<SYNC) ; SYNC-Flag set
CLR chAddressIn ; Clear chAddressIn
ChkSyncExit:Clear16Reg pulseInH,pulseInL ; Clear pulseIn register
RestoreTempFromStack
RETI
ChkError: Load16Temp MinPulseTime
Compare16Temp pulseInH,pulseInL ; Check if pulseIn > MinPulseTime
BRCC Error
Load16Temp MaxPulseTime
Compare16Temp pulseInH,pulseInL ; Check if pulseIn < MaxPulseTime
BRCS Error
CPI pulseError,0 ; Check if pulseError == 0
BREQ NoError
DEC pulseError ; Decrement pulseError
INC chAddressIn ; Increment pulseOut-address twice
INC chAddressIn
CPI chAddressIn,(NrOfChannels*2) ; Check if chAddress == (NrOfChannels*2)
BRNE ChkErrExit
CBR pulseFlag,(1<<SYNC) ; Clear SYNC-Flag
Clear16Reg frameCalcH,frameCalcL ; Clear FrameCalculation register
ChkErrExit: Clear16Reg pulseInH,pulseInL ; Clear pulseIn register
RestoreTempFromStack
RETI
Error: CBR pulseFlag,(1<<SYNC) ; SYNC-Flagg cleared
SBR pulseFlag,(1<<LEDredActive) ; LEDActive-Flag set
LDI pulseError,(RecoveryNr*NrOfChannels); pulseError = (RecoveryNr*NrOfChannels)
ErrorExit: Clear16Reg pulseInH,pulseInL ; Clear pulseIn register
RestoreTempFromStack
RETI
NoError: CLR timeout ; Clear timeout register
CLR chAddressFS ; Clear chAddressEE
StartTimer0 ; Start timer0
Read16SRAM chAddressIn,tempH,tempL ; Copy SRAM to temp
Add16Reg pulseInH,pulseInL,tempH,tempL ; Add past value to current value
DivByTwo16Reg pulseInH,pulseInL ; Divide value by two
Write16SRAM chAddressIn,pulseInH,pulseInL ; Copy temp to SRAM
Add16Reg frameCalcH,frameCalcL,pulseInH,pulseInL; Add curren value to frameCalc register
INC chAddressIn ; Increment pulseOut-address twice
INC chAddressIn
CPI chAddressIn,(NrOfChannels*2) ; Check if chAddress == (NrOfChannels*2)
BRNE NoErrorExit
CBR pulseFlag,(1<<SYNC) ; Clear SYNC-Flag
Load16Temp FrameTime
Sub16Reg tempH,tempL,frameCalcH,frameCalcL ; Calculate frame-time
Write16SRAM chAddressIn,tempH,tempL ; Write frame-time to SRAM
SwitchBuffert ; Switch Buffert in SRAM
CBR pulseFlag,(1<<LEDredActive) ; LEDredActive-Flag cleared
CBR pulseFlag,(1<<LEDyellowActive) ; LEDywllowActive-Flag cleared
Clear16Reg frameCalcH,frameCalcL ; Clear frameCalc register
NoErrorExit:Clear16Reg pulseInH,pulseInL ; Clear pulseIn register
RestoreTempFromStack
RETI
;----------------------------------------------------------------------------------------------------------
; Timer_Int1
;
; Description: Increment pulseIn and pulseOut counter-registers
;----------------------------------------------------------------------------------------------------------
Timer_Int1: ADIW pulseInL,1 ; Increment pulseIn register
ADIW pulseOutL,1 ; Increment pulseOut register
;----------------------------------------------------------------------------------------------------------
; Timer_Int0
;
; Description: Failsafe "watchdog". Restore pulse-value from EEPROM to SRAM if timeout occurs.
;----------------------------------------------------------------------------------------------------------
Timer_Int0: StoreTempOnStack
CPI timeout,FStimeoutTime ; Check if timeout == FailsafeTime
BREQ CopyPulseES
INC timeout ; Increment timeout
RestoreTempFromStack
RETI
CopyPulseES:SBR pulseFlag,(1<<LEDyellowActive) ; LEDActive-Flgg set
Read16EEPROM chAddressFS,tempH,tempL ; Copy EEPROM to temp
Write16SRAM chAddressFS,tempH,tempL ; Copy temp to SRAM
SwitchBuffert ; Switch Buffert in SRAM
Write16SRAM chAddressFS,tempH,tempL ; Copy temp to SRAM
SwitchBuffert ; Switch Buffert in SRAM
INC chAddressFS ; Increment chAddressFS twice
INC chAddressFS
CPI chAddressFS,((NrOfChannels*2)+2) ; Check if chAddressFS ==
BRNE NextPulseES ; ((NrOfChannels*2)+2)
StopTimer0 ; Stop timer0
NextPulseES:RestoreTempFromStack
RETI
;==========================================================================================================
; Main
;==========================================================================================================
Main: LDI temp,LOW(RAMEND)
OUT SPL,temp ; Set StackPointeradress
CLI ; Disable GlobalInterrupt
LDI temp,0xFF
OUT DDRB,temp ; All output on PORTB
SBI DDRD,LEDred ; All input except PIN"LEDred"
SBI DDRD,LEDyellow ; and PIN"LEDyellow"
SBI PORTD,PPMSignal ; Enable pullup on INT0 input
SBI PORTD,StoreButton ; Enable pullup on StoreButton input
LDI temp,(1<<INT0)
OUT GIMSK,temp ; Enable external interrupt 0
LDI temp,(1<<ISC01)
OUT MCUCR,temp ; Trigg on falling edge
LDI temp,(1<<OCIE1A)|(1<<TOIE0)
OUT TIMSK,temp ; Enable internal timer0
; and timer1 interrupt
CLR chAddressOut ; Clear register
CLR chAddressIn
CLR chAddressFS
CLR timeout
CLR pulseFlag
CLR ZH ; Clear Z-Pointer MSB
Clear16Reg pulseInH,pulseInL ; Clear pulseIn-Reg
Clear16Reg pulseOutH,pulseOutL ; Clear pulseOut-Reg
LDI temp,(LOW(SRAMAddress2)-SRAMAddress1)
MOV nextSRAMAddress,temp ; Set nextSRAMAddress
InitMemES: Read16EEPROM chAddressEE,tempH,tempL ; Copy pulse-value from EEPROM to SRAM
Write16SRAM chAddressEE,tempH,tempL
SwitchBuffert ; Switch Buffert in SRAM
Write16SRAM chAddressEE,tempH,tempL
SwitchBuffert ; Switch Buffert in SRAM
INC chAddressEE
INC chAddressEE
CPI chAddressEE,((NrOfChannels*2)+2);
BRNE InitMemES
CBR pulseFlag,(1<<SYNC) ; Generate error
SBR pulseFlag,(1<<LEDredActive)
LDI pulseError,(RecoveryNr*NrOfChannels)
SEI ; Enable GlobalInterrupt
StartTimer1 ; Start timer1
StartTimer0 ; Start timer0
;----------------------------------------------------------------------------------------------------------
; MainLoop
;----------------------------------------------------------------------------------------------------------
MainLoop:
; Generate output-pulses from SRAM to PORTB.
CPI chAddressOut,0 ; Check if chAddressOut == 0
BRNE ReadOPulse
LDI temp,0x01
OUT PORTB,temp ; PORTB = 0x01
ReadOPulse: ;SwitchBuffert ; Switch Buffert in SRAM
Read16SRAM chAddressOut,tempH,tempL ; Copy SRAM to temp
;SwitchBuffert ; Switch Buffert in SRAM
Compare16Temp pulseOutH,pulseOutL ; Check if pulseOut < temp
BRCS NextOPulse
RJMP ExitOPulse
NextOPulse: CPI chAddressOut,(NrOfChannels*2) ; Check if chAddressOut ==
BREQ ResetOPulse ; (NrOfChannels*2)
IN temp,PORTB
LSL temp
OUT PORTB,temp ; Roll PORTB left
INC chAddressOut ; Increment chAddressOut twice
INC chAddressOut
Clear16Reg pulseOutH,pulseOutL ; Clear pulseOut register
RJMP ExitOPulse
ResetOPulse:CLR chAddressOut ; Clear chAddressOut register
Clear16Reg pulseOutH,pulseOutL ; Clear pulseOut register
ExitOPulse: RJMP MainLoop
;output yellow led-status
LEDy: SBRC pulseFlag,LEDyellowActive ; Check if LEDyellowActive is set
RJMP LEDyON
LEDyOFF: CBI PORTD,LEDyellow ; LEDyellow OFF (No Error)
RJMP LEDr
LEDyON: SBI PORTD,LEDyellow ; LEDyellow ON (Error detected)
;output red led-status
LEDr: SBRC pulseFlag,LEDredActive ; Check if LEDredActive is set
RJMP LEDrON
LEDrOFF: CBI PORTD,LEDred ; LEDred OFF (No Error)
RJMP CheckButton
LEDrON: SBI PORTD,LEDred ; LEDred ON (Error detected)
RJMP MainLoop
CheckButton:SBIC PIND,StoreButton ; Check if button pressed
CLR debounceFilter ; Clear debounce register
INC debounceFilter ; Incerment debounce register
BRNE MainLoop
BPressed: CLI ; Disable GlobalInterrupt
SBI PORTD,LEDyellow ; Led ON (Work in progress)
CLR temp
OUT PORTB,temp ; PORTB = 0x00
CLR chAddressEE ; Clear chAddressEE register
SwitchBuffert ; Switch Buffert in SRAM
CopyPulseSE:Read16SRAM chAddressEE,tempH,tempL ; Copy SRAM to temp
Write16EEPROM chAddressEE,tempH,tempL ; Copy temp to EEPROM
INC chAddressEE ; Increment chAddressEE twice
INC chAddressEE
CPI chAddressEE,((NrOfChannels*2)+2) ; Check if chAddressFE ==
BRNE CopyPulseSE ; ((NrOfChannels*2)+2)
CBI PORTD,LEDyellow ; Led OFF (Work complete)
SwitchBuffert ; Switch Buffert in SRAM
ButtonLoop: SBIS PIND,StoreButton ; Check if button pressed
RJMP ButtonLoop
SEI ; Enable GlobalInterrupt
RJMP MainLoop
;==========================================================================================================
; END
;==========================================================================================================