mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git
synced 2024-12-27 17:32:31 +00:00
75cc3ef8ba
When sync ADC and the DAC was running at the same time the ADC showed spikes in the signal. This happened when just before the DRDY from the ADC was triggered a DAC interrupt was dealt with. ADC and DAC share the same SPI bus and priority is now given the ADC. The DAC values are now first stored in a buffer and are only send to the DAC once the ADC has finished converting all channels (start = 0) so that the SPI bus is definitely quiet for about 100us. Signed-off-by: Bernd Porr <mail@berndporr.me.uk> Signed-off-by: Kyle McMartin <kyle@kernel.org>
1395 lines
28 KiB
NASM
1395 lines
28 KiB
NASM
; usbdux_firmware.asm
|
|
; Copyright (C) 2010,2015 Bernd Porr, mail@berndporr.me.uk
|
|
; For usbduxsigma.c 0.5+
|
|
;
|
|
; This program is free software; you can redistribute it and/or modify
|
|
; it under the terms of the GNU General Public License as published by
|
|
; the Free Software Foundation; either version 2 of the License, or
|
|
; (at your option) any later version.
|
|
;
|
|
; This program is distributed in the hope that it will be useful,
|
|
; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
; GNU General Public License for more details.
|
|
;
|
|
; You should have received a copy of the GNU General Public License
|
|
; along with this program; if not, write to the Free Software
|
|
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
;
|
|
;
|
|
; Firmware: usbduxsigma_firmware.asm for usbduxsigma.c
|
|
; Description: University of Stirling USB DAQ & INCITE Technology Limited
|
|
; Devices: [ITL] USB-DUX-SIGMA (usbduxsigma.ko)
|
|
; Author: Bernd Porr <mail@berndporr.me.uk>
|
|
; Updated: 20 Jul 2015
|
|
; Status: testing
|
|
;
|
|
;;;
|
|
;;;
|
|
;;;
|
|
|
|
.inc fx2-include.asm
|
|
|
|
;;; a couple of flags in high memory
|
|
.equ CMD_FLAG,80h ; flag for the next in transfer
|
|
.equ PWMFLAG,81h ; PWM on or off?
|
|
.equ MAXSMPL,82H ; maximum number of samples, n channellist
|
|
.equ MUXSG0,83H ; content of the MUXSG0 register
|
|
.equ INTERVAL,88h ; uframe/frame interval
|
|
.equ INTCTR,89h ; interval counter
|
|
.equ DABUFFER,0F0h ; buffer with DA values
|
|
|
|
;;; in precious low memory but accessible within one clock cycle
|
|
.equ DPTRL,70H
|
|
.equ DPTRH,71h
|
|
.equ ASYNC_ON,72h
|
|
.equ SMPLCTR,73h
|
|
|
|
;;; actual code
|
|
.org 0000h ; after reset the processor starts here
|
|
ljmp main ; jump to the main loop
|
|
|
|
.org 0003h
|
|
ljmp isr0 ; external interrupt 0: /DRY
|
|
|
|
.org 0043h ; the IRQ2-vector
|
|
ljmp jmptbl ; irq service-routine
|
|
|
|
.org 0100h ; start of the jump table
|
|
|
|
jmptbl: ljmp sudav_isr
|
|
nop
|
|
ljmp sof_isr
|
|
nop
|
|
ljmp sutok_isr
|
|
nop
|
|
ljmp suspend_isr
|
|
nop
|
|
ljmp usbreset_isr
|
|
nop
|
|
ljmp hispeed_isr
|
|
nop
|
|
ljmp ep0ack_isr
|
|
nop
|
|
ljmp spare_isr
|
|
nop
|
|
ljmp ep0in_isr
|
|
nop
|
|
ljmp ep0out_isr
|
|
nop
|
|
ljmp ep1in_isr
|
|
nop
|
|
ljmp ep1out_isr
|
|
nop
|
|
ljmp ep2_isr
|
|
nop
|
|
ljmp ep4_isr
|
|
nop
|
|
ljmp ep6_isr
|
|
nop
|
|
ljmp ep8_isr
|
|
nop
|
|
ljmp ibn_isr
|
|
nop
|
|
ljmp spare_isr
|
|
nop
|
|
ljmp ep0ping_isr
|
|
nop
|
|
ljmp ep1ping_isr
|
|
nop
|
|
ljmp ep2ping_isr
|
|
nop
|
|
ljmp ep4ping_isr
|
|
nop
|
|
ljmp ep6ping_isr
|
|
nop
|
|
ljmp ep8ping_isr
|
|
nop
|
|
ljmp errlimit_isr
|
|
nop
|
|
ljmp spare_isr
|
|
nop
|
|
ljmp spare_isr
|
|
nop
|
|
ljmp spare_isr
|
|
nop
|
|
ljmp ep2isoerr_isr
|
|
nop
|
|
ljmp ep4isoerr_isr
|
|
nop
|
|
ljmp ep6isoerr_isr
|
|
nop
|
|
ljmp ep8isoerr_isr
|
|
|
|
|
|
;; dummy isr
|
|
sudav_isr:
|
|
sutok_isr:
|
|
suspend_isr:
|
|
usbreset_isr:
|
|
hispeed_isr:
|
|
ep0ack_isr:
|
|
spare_isr:
|
|
ep0in_isr:
|
|
ep0out_isr:
|
|
ibn_isr:
|
|
ep0ping_isr:
|
|
ep1ping_isr:
|
|
ep2ping_isr:
|
|
ep4ping_isr:
|
|
ep6ping_isr:
|
|
ep8ping_isr:
|
|
errlimit_isr:
|
|
ep2isoerr_isr:
|
|
ep4isoerr_isr:
|
|
ep6isoerr_isr:
|
|
ep8isoerr_isr:
|
|
ep6_isr:
|
|
ep2_isr:
|
|
ep4_isr:
|
|
|
|
push dps
|
|
push dpl
|
|
push dph
|
|
push dpl1
|
|
push dph1
|
|
push acc
|
|
push psw
|
|
|
|
;; clear the USB2 irq bit and return
|
|
mov a,EXIF
|
|
clr acc.4
|
|
mov EXIF,a
|
|
|
|
pop psw
|
|
pop acc
|
|
pop dph1
|
|
pop dpl1
|
|
pop dph
|
|
pop dpl
|
|
pop dps
|
|
|
|
reti
|
|
|
|
|
|
ep1in_isr:
|
|
push dps
|
|
push dpl
|
|
push dph
|
|
push dpl1
|
|
push dph1
|
|
push acc
|
|
push psw
|
|
|
|
mov dptr,#0E7C0h ; EP1in
|
|
mov a,IOB ; get DIO D
|
|
movx @dptr,a ; store it
|
|
inc dptr ; next byte
|
|
mov a,IOC ; get DIO C
|
|
movx @dptr,a ; store it
|
|
inc dptr ; next byte
|
|
mov a,IOD ; get DIO B
|
|
movx @dptr,a ; store it
|
|
inc dptr ; next byte
|
|
mov a,#0 ; just zero
|
|
movx @dptr,a ; pad it up
|
|
|
|
;; clear INT2
|
|
mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
|
|
clr acc.4
|
|
mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
|
|
|
|
mov DPTR,#EPIRQ ;
|
|
mov a,#00000100b ; clear the ep1in
|
|
movx @DPTR,a
|
|
|
|
pop psw
|
|
pop acc
|
|
pop dph1
|
|
pop dpl1
|
|
pop dph
|
|
pop dpl
|
|
pop dps
|
|
reti
|
|
|
|
|
|
|
|
;;; this is triggered when DRY goes low
|
|
isr0:
|
|
push dps
|
|
push dpl
|
|
push dph
|
|
push dpl1
|
|
push dph1
|
|
push acc
|
|
push psw
|
|
push 00h ; R0
|
|
push 01h ; R1
|
|
push 02h ; R2
|
|
push 03h ; R3
|
|
push 04h ; R4
|
|
push 05h ; R5
|
|
push 06h ; R6
|
|
push 07h ; R7
|
|
|
|
mov a,ASYNC_ON
|
|
jz noepsubmit
|
|
|
|
mov DPS,#0
|
|
mov dpl,DPTRL
|
|
mov dph,DPTRH
|
|
|
|
lcall readADCch ; read one channel
|
|
|
|
mov DPTRL,dpl
|
|
mov DPTRH,dph
|
|
|
|
mov a,SMPLCTR
|
|
dec a
|
|
mov SMPLCTR,a
|
|
jnz noepsubmit
|
|
|
|
mov ASYNC_ON,#0
|
|
|
|
clr IOA.7 ; START = 0
|
|
|
|
;; arm the endpoint and send off the data
|
|
mov DPTR,#EP6BCH ; byte count H
|
|
mov a,#0 ; is zero
|
|
lcall syncdelaywr ; wait until we can write again
|
|
|
|
mov r0,#MAXSMPL ; number of samples to transmit
|
|
mov a,@r0 ; get them
|
|
rl a ; a=a*2
|
|
rl a ; a=a*2
|
|
add a,#4 ; four bytes for DIO
|
|
mov DPTR,#EP6BCL ; byte count L
|
|
lcall syncdelaywr ; wait until we can write again
|
|
|
|
noepsubmit:
|
|
pop 07h
|
|
pop 06h
|
|
pop 05h
|
|
pop 04h ; R4
|
|
pop 03h ; R3
|
|
pop 02h ; R2
|
|
pop 01h ; R1
|
|
pop 00h ; R0
|
|
pop psw
|
|
pop acc
|
|
pop dph1
|
|
pop dpl1
|
|
pop dph
|
|
pop dpl
|
|
pop dps
|
|
|
|
reti
|
|
|
|
|
|
|
|
;;; main program
|
|
;;; basically only initialises the processor and
|
|
;;; then engages in an endless loop
|
|
main:
|
|
mov DPTR,#CPUCS ; CPU control register
|
|
mov a,#00010000b ; 48Mhz
|
|
lcall syncdelaywr
|
|
|
|
mov dptr,#REVCTL
|
|
mov a,#00000011b ; allows skip
|
|
lcall syncdelaywr
|
|
|
|
mov dptr,#INTSETUP ; IRQ setup register
|
|
mov a,#08h ; enable autovector
|
|
lcall syncdelaywr
|
|
|
|
mov dptr,#PORTCCFG
|
|
mov a,#0
|
|
lcall syncdelaywr
|
|
|
|
mov IP,#01H ; int0 has highest interrupt priority
|
|
mov EIP,#0 ; all USB interrupts have low priority
|
|
|
|
lcall initAD ; init the ports to the converters
|
|
|
|
lcall initeps ; init the isochronous data-transfer
|
|
|
|
;;; main loop, rest is done as interrupts
|
|
mloop2: nop
|
|
|
|
;;; pwm
|
|
mov r0,#PWMFLAG ; pwm on?
|
|
mov a,@r0 ; get info
|
|
jz mloop2 ; it's off
|
|
|
|
mov a,GPIFTRIG ; GPIF status
|
|
anl a,#80h ; done bit
|
|
jz mloop2 ; GPIF still busy
|
|
|
|
mov a,#01h ; WR,EP4, 01 = EP4
|
|
mov GPIFTRIG,a ; restart it
|
|
|
|
sjmp mloop2 ; loop for ever
|
|
|
|
|
|
;;; initialise the ports for the AD-converter
|
|
initAD:
|
|
mov r0,#MAXSMPL ; length of channellist
|
|
mov @r0,#0 ; we don't want to accumlate samples
|
|
|
|
mov ASYNC_ON,#0 ; async enable
|
|
|
|
mov r0,#DABUFFER
|
|
mov @r0,#0
|
|
|
|
mov OEA,#11100000b ; PortA7,A6,A5 Outputs
|
|
mov IOA,#01100000b ; /CS = 1 and START = 0
|
|
mov dptr,#IFCONFIG ; switch on clock on IFCLK pin
|
|
mov a,#10100000b ; gpif, 30MHz, internal IFCLK -> 15MHz for AD
|
|
lcall syncdelaywr
|
|
|
|
mov SCON0,#013H ; ser rec en, TX/RX: stop, 48/12MHz=4MHz clock
|
|
|
|
mov dptr,#PORTECFG
|
|
mov a,#00001000b ; special function for port E: RXD0OUT
|
|
lcall syncdelaywr
|
|
|
|
ret
|
|
|
|
|
|
;;; send a byte via SPI
|
|
;;; content in a, dptr1 is changed
|
|
;;; the lookup is done in dptr1 so that the normal dptr is not affected
|
|
;;; important: /cs needs to be reset to 1 by the caller: IOA.5
|
|
sendSPI:
|
|
inc DPS
|
|
|
|
;; bit reverse
|
|
mov dptr,#swap_lut ; lookup table
|
|
movc a,@a+dptr ; reverse bits
|
|
|
|
;; clear interrupt flag, is used to detect
|
|
;; successful transmission
|
|
clr SCON0.1 ; clear interrupt flag
|
|
|
|
;; start transmission by writing the byte
|
|
;; in the transmit buffer
|
|
mov SBUF0,a ; start transmission
|
|
|
|
;; wait for the end of the transmission
|
|
sendSPIwait:
|
|
mov a,SCON0 ; get transmission status
|
|
jnb ACC.1,sendSPIwait ; loop until transmitted
|
|
|
|
inc DPS
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
;;; receive a byte via SPI
|
|
;;; content in a, dptr is changed
|
|
;;; the lookup is done in dptr1 so that the normal dptr is not affected
|
|
;;; important: the /CS needs to be set to 1 by the caller via "setb IOA.5"
|
|
recSPI:
|
|
inc DPS
|
|
|
|
clr IOA.5 ; /cs to 0
|
|
|
|
;; clearning the RI bit starts reception of data
|
|
clr SCON0.0
|
|
|
|
recSPIwait:
|
|
;; RI goes back to 1 after the reception of the 8 bits
|
|
mov a,SCON0 ; get receive status
|
|
jnb ACC.0,recSPIwait; loop until all bits received
|
|
|
|
;; read the byte from the buffer
|
|
mov a,SBUF0 ; get byte
|
|
|
|
;; lookup: reverse the bits
|
|
mov dptr,#swap_lut ; lookup table
|
|
movc a,@a+dptr ; reverse the bits
|
|
|
|
inc DPS
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
;;; reads a register
|
|
;;; register address in a
|
|
;;; returns value in a
|
|
registerRead:
|
|
anl a,#00001111b ; mask out the index to the register
|
|
orl a,#01000000b ; 010xxxxx indicates register read
|
|
clr IOA.5 ; ADC /cs to 0
|
|
lcall sendSPI ; send the command over
|
|
lcall recSPI ; read the contents back
|
|
setb IOA.5 ; ADC /cs to 1
|
|
ret
|
|
|
|
|
|
|
|
;;; writes to a register
|
|
;;; register address in a
|
|
;;; value in r0
|
|
registerWrite:
|
|
push acc
|
|
anl a,#00001111b ; mask out the index to the register
|
|
orl a,#01100000b ; 011xxxxx indicates register write
|
|
|
|
clr IOA.5 ; ADC /cs to 0
|
|
|
|
lcall sendSPI ;
|
|
mov a,r0
|
|
lcall sendSPI
|
|
|
|
setb IOA.5 ; ADC /cs to 1
|
|
pop acc
|
|
|
|
lcall registerRead ; check if the data has arrived in the ADC
|
|
mov 0f0h,r0 ; register B
|
|
cjne a,0f0h,registerWrite ; something went wrong, try again
|
|
|
|
ret
|
|
|
|
|
|
|
|
;;; initilise the endpoints
|
|
initeps:
|
|
mov dptr,#FIFORESET
|
|
mov a,#80H
|
|
movx @dptr,a ; reset all fifos
|
|
mov a,#2
|
|
movx @dptr,a ;
|
|
mov a,#4
|
|
movx @dptr,a ;
|
|
mov a,#6
|
|
movx @dptr,a ;
|
|
mov a,#8
|
|
movx @dptr,a ;
|
|
mov a,#0
|
|
movx @dptr,a ; normal operat
|
|
|
|
mov DPTR,#EP2CFG
|
|
mov a,#10010010b ; valid, out, double buff, iso
|
|
movx @DPTR,a
|
|
|
|
mov dptr,#EP2FIFOCFG
|
|
mov a,#00000000b ; manual
|
|
movx @dptr,a
|
|
|
|
mov dptr,#EP2BCL ; "arm" it
|
|
mov a,#00h
|
|
movx @DPTR,a ; can receive data
|
|
lcall syncdelay ; wait to sync
|
|
movx @DPTR,a ; can receive data
|
|
lcall syncdelay ; wait to sync
|
|
movx @DPTR,a ; can receive data
|
|
lcall syncdelay ; wait to sync
|
|
|
|
mov DPTR,#EP1OUTCFG
|
|
mov a,#10100000b ; valid
|
|
movx @dptr,a
|
|
|
|
mov dptr,#EP1OUTBC ; "arm" it
|
|
mov a,#00h
|
|
movx @DPTR,a ; can receive data
|
|
lcall syncdelay ; wait until we can write again
|
|
movx @dptr,a ; make shure its really empty
|
|
lcall syncdelay ; wait
|
|
|
|
mov DPTR,#EP6CFG ; ISO data from here to the host
|
|
mov a,#11010010b ; Valid
|
|
movx @DPTR,a ; ISO transfer, double buffering
|
|
|
|
mov DPTR,#EP8CFG ; EP8
|
|
mov a,#11100000b ; BULK data from here to the host
|
|
movx @DPTR,a ;
|
|
|
|
mov dptr,#PORTACFG
|
|
mov a,#1 ; interrupt on pin A0
|
|
lcall syncdelaywr
|
|
|
|
;; enable interrupts
|
|
mov dptr,#EPIE ; interrupt enable
|
|
mov a,#10001100b ; enable irq for ep1out,8,ep1in
|
|
movx @dptr,a ; do it
|
|
|
|
mov dptr,#EPIRQ ; clear IRQs
|
|
mov a,#10001100b
|
|
movx @dptr,a
|
|
|
|
mov DPTR,#USBIE ; USB int enables register
|
|
mov a,#2 ; enables SOF (1ms/125us interrupt)
|
|
movx @DPTR,a ;
|
|
|
|
setb TCON.0 ; make INT0 edge triggered, falling edge
|
|
|
|
mov EIE,#00000001b ; enable INT2/USBINT in the 8051's SFR
|
|
mov IE,#81h ; IE, enable all interrupts and INT0
|
|
|
|
ret
|
|
|
|
|
|
;;; Reads one ADC channel from the converter and stores
|
|
;;; the result at dptr
|
|
readADCch:
|
|
;; reading data is done by just dropping /CS and start reading and
|
|
;; while keeping the IN signal to the ADC inactive
|
|
clr IOA.5 ; /cs to 0
|
|
|
|
;; 1st byte: STATUS
|
|
lcall recSPI ; index
|
|
movx @dptr,a ; store the byte
|
|
inc dptr ; increment pointer
|
|
|
|
;; 2nd byte: MSB
|
|
lcall recSPI ; data
|
|
movx @dptr,a
|
|
inc dptr
|
|
|
|
;; 3rd byte: MSB-1
|
|
lcall recSPI ; data
|
|
movx @dptr,a
|
|
inc dptr
|
|
|
|
;; 4th byte: LSB
|
|
lcall recSPI ; data
|
|
movx @dptr,a
|
|
inc dptr
|
|
|
|
;; got all bytes
|
|
setb IOA.5 ; /cs to 1
|
|
|
|
ret
|
|
|
|
|
|
|
|
;;; interrupt-routine for SOF
|
|
sof_isr:
|
|
push dps
|
|
push dpl
|
|
push dph
|
|
push dpl1
|
|
push dph1
|
|
push acc
|
|
push psw
|
|
push 00h ; R0
|
|
push 01h ; R1
|
|
push 02h ; R2
|
|
push 03h ; R3
|
|
push 04h ; R4
|
|
push 05h ; R5
|
|
push 06h ; R6
|
|
push 07h ; R7
|
|
|
|
mov r0,#INTCTR ; interval counter
|
|
mov a,@r0 ; get the value
|
|
dec a ; decrement
|
|
mov @r0,a ; save it again
|
|
jz sof_adc ; we do ADC functions
|
|
ljmp epfull ; we skip all adc functions
|
|
|
|
sof_adc:
|
|
mov r1,#INTERVAL ; get the interval
|
|
mov a,@r1 ; get it
|
|
mov @r0,a ; save it in the counter
|
|
mov a,EP2468STAT
|
|
anl a,#20H ; full?
|
|
jnz epfull ; EP6-buffer is full
|
|
|
|
mov a,IOA ; conversion running?
|
|
jb ACC.7,epfull
|
|
|
|
;; make sure that we are starting with the first channel
|
|
mov r0,#MUXSG0 ;
|
|
mov a,@r0 ; get config of MUXSG0
|
|
mov r0,a
|
|
mov a,#04H ; MUXSG0
|
|
lcall registerWrite ; this resets the channel sequence
|
|
|
|
setb IOA.7 ; start converter, START = 1
|
|
|
|
mov dptr,#0f800h ; EP6 buffer
|
|
mov a,IOD ; get DIO D
|
|
movx @dptr,a ; store it
|
|
inc dptr ; next byte
|
|
mov a,IOC ; get DIO C
|
|
movx @dptr,a ; store it
|
|
inc dptr ; next byte
|
|
mov a,IOB ; get DIO B
|
|
movx @dptr,a ; store it
|
|
inc dptr ; next byte
|
|
mov a,#0 ; just zero
|
|
movx @dptr,a ; pad it up
|
|
inc dptr ; algin along a 32 bit word
|
|
mov DPTRL,dpl
|
|
mov DPTRH,dph
|
|
|
|
mov r0,#MAXSMPL
|
|
mov a,@r0
|
|
mov SMPLCTR,a
|
|
|
|
mov ASYNC_ON,#1
|
|
|
|
epfull:
|
|
;; do the D/A conversion
|
|
mov a,EP2468STAT
|
|
anl a,#01H ; empty
|
|
jnz epempty ; nothing to get
|
|
|
|
mov dptr,#0F000H ; EP2 fifo buffer
|
|
lcall dalo ; conversion
|
|
|
|
mov dptr,#EP2BCL ; "arm" it
|
|
mov a,#00h
|
|
lcall syncdelaywr ; wait for the rec to sync
|
|
lcall syncdelaywr ; wait for the rec to sync
|
|
|
|
epempty:
|
|
mov a,IOA ; conversion running?
|
|
jb ACC.7,sofend
|
|
|
|
lcall DAsend
|
|
|
|
sofend:
|
|
;; clear INT2
|
|
mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
|
|
clr acc.4
|
|
mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
|
|
|
|
mov DPTR,#USBIRQ ; points to the SOF
|
|
mov a,#2 ; clear the SOF
|
|
movx @DPTR,a
|
|
|
|
nosof:
|
|
pop 07h
|
|
pop 06h
|
|
pop 05h
|
|
pop 04h ; R4
|
|
pop 03h ; R3
|
|
pop 02h ; R2
|
|
pop 01h ; R1
|
|
pop 00h ; R0
|
|
pop psw
|
|
pop acc
|
|
pop dph1
|
|
pop dpl1
|
|
pop dph
|
|
pop dpl
|
|
pop dps
|
|
reti
|
|
|
|
|
|
reset_ep8:
|
|
;; erase all data in ep8
|
|
mov dptr,#FIFORESET
|
|
mov a,#80H ; NAK
|
|
lcall syncdelaywr
|
|
mov dptr,#FIFORESET
|
|
mov a,#8 ; reset EP8
|
|
lcall syncdelaywr
|
|
mov dptr,#FIFORESET
|
|
mov a,#0 ; normal operation
|
|
lcall syncdelaywr
|
|
ret
|
|
|
|
|
|
reset_ep6:
|
|
;; throw out old data
|
|
mov dptr,#FIFORESET
|
|
mov a,#80H ; NAK
|
|
lcall syncdelaywr
|
|
mov dptr,#FIFORESET
|
|
mov a,#6 ; reset EP6
|
|
lcall syncdelaywr
|
|
mov dptr,#FIFORESET
|
|
mov a,#0 ; normal operation
|
|
lcall syncdelaywr
|
|
ret
|
|
|
|
|
|
;;; configure the ADC converter
|
|
;;; the dptr points to the init data:
|
|
;;; CONFIG 0,1,3,4,5,6
|
|
;;; note that CONFIG2 is omitted
|
|
configADC:
|
|
clr IOA.7 ; stops ADC: START line of ADC = L
|
|
setb IOA.5 ; ADC /cs to 1
|
|
|
|
;; just in case something has gone wrong
|
|
nop
|
|
nop
|
|
nop
|
|
|
|
mov a,#11000000b ; reset the ADC
|
|
clr IOA.5 ; ADC /cs to 0
|
|
lcall sendSPI
|
|
setb IOA.5 ; ADC /cs to 1
|
|
|
|
movx a,@dptr ;
|
|
inc dptr
|
|
mov r0,a
|
|
mov a,#00H ; CONFIG0
|
|
lcall registerWrite
|
|
|
|
movx a,@dptr ;
|
|
inc dptr
|
|
mov r0,a
|
|
mov a,#01H ; CONFIG1
|
|
lcall registerWrite
|
|
|
|
movx a,@dptr ;
|
|
inc dptr
|
|
mov r0,a
|
|
mov a,#03H ; MUXDIF
|
|
lcall registerWrite
|
|
|
|
movx a,@dptr ;
|
|
inc dptr
|
|
mov r0,#MUXSG0
|
|
mov @r0,a ; store it for reset purposes
|
|
mov r0,a
|
|
mov a,#04H ; MUXSG0
|
|
lcall registerWrite
|
|
|
|
movx a,@dptr ;
|
|
inc dptr
|
|
mov r0,a
|
|
mov a,#05H ; MUXSG1
|
|
lcall registerWrite
|
|
|
|
movx a,@dptr ;
|
|
inc dptr
|
|
mov r0,a
|
|
mov a,#06H ; SYSRED
|
|
lcall registerWrite
|
|
|
|
ret
|
|
|
|
|
|
;;; interrupt-routine for ep1out
|
|
;;; receives the channel list and other commands
|
|
ep1out_isr:
|
|
push dps
|
|
push dpl
|
|
push dph
|
|
push dpl1
|
|
push dph1
|
|
push acc
|
|
push psw
|
|
push 00h ; R0
|
|
push 01h ; R1
|
|
push 02h ; R2
|
|
push 03h ; R3
|
|
push 04h ; R4
|
|
push 05h ; R5
|
|
push 06h ; R6
|
|
push 07h ; R7
|
|
|
|
mov dptr,#0E780h ; FIFO buffer of EP1OUT
|
|
movx a,@dptr ; get the first byte
|
|
mov r0,#CMD_FLAG ; pointer to the command byte
|
|
mov @r0,a ; store the command byte for ep8
|
|
|
|
mov dptr,#ep1out_jmp; jump table for the different functions
|
|
rl a ; multiply by 2: sizeof sjmp
|
|
jmp @a+dptr ; jump to the jump table
|
|
;; jump table, corresponds to the command bytes defined
|
|
;; in usbdux.c
|
|
ep1out_jmp:
|
|
sjmp startadc ; a=0
|
|
sjmp single_da ; a=1
|
|
sjmp config_digital_b; a=2
|
|
sjmp write_digital_b ; a=3
|
|
sjmp initsgADchannel ; a=4
|
|
sjmp nothing ; a=5
|
|
sjmp nothing ; a=6
|
|
sjmp pwm_on ; a=7
|
|
sjmp pwm_off ; a=8
|
|
sjmp startadcint ; a=9
|
|
|
|
nothing:
|
|
ljmp over_da
|
|
|
|
pwm_on:
|
|
lcall startPWM
|
|
sjmp over_da
|
|
|
|
pwm_off:
|
|
lcall stopPWM
|
|
sjmp over_da
|
|
|
|
initsgADchannel:
|
|
mov ASYNC_ON,#0
|
|
|
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT
|
|
lcall configADC ; configures the ADC esp sel the channel
|
|
|
|
lcall reset_ep8 ; reset FIFO: get rid of old bytes
|
|
;; Save new A/D data in EP8. This is the first byte
|
|
;; the host will read during an INSN. If there are
|
|
;; more to come they will be handled by the ISR of
|
|
;; ep8.
|
|
lcall ep8_ops ; get A/D data
|
|
|
|
sjmp over_da
|
|
|
|
startadcint:
|
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte
|
|
|
|
movx a,@dptr ; interval is the 1st byte
|
|
inc dptr ; data pointer
|
|
sjmp startadc2 ; the other paramters as with startadc
|
|
|
|
;;; config AD:
|
|
;;; we write to the registers of the A/D converter
|
|
startadc:
|
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT from 2nd byte
|
|
|
|
mov a,#1 ; interval is 1 here all the time
|
|
startadc2:
|
|
mov r0,#INTERVAL ; set it
|
|
mov @r0,a
|
|
mov r0,#INTCTR ; the counter is also just one
|
|
mov @r0,a
|
|
|
|
movx a,@dptr ; get length of channel list
|
|
inc dptr
|
|
mov r0,#MAXSMPL
|
|
mov @r0,a ; length of the channel list
|
|
mov SMPLCTR,a
|
|
|
|
lcall configADC ; configures all registers
|
|
|
|
mov ASYNC_ON,#1 ; async enable
|
|
|
|
lcall reset_ep6 ; reset FIFO
|
|
|
|
;; load new A/D data into EP6
|
|
;; This must be done. Otherwise the ISR is never called.
|
|
;; The ISR is only called when data has _left_ the
|
|
;; ep buffer here it has to be refilled.
|
|
lcall ep6_arm ; fill with dummy data
|
|
|
|
sjmp over_da
|
|
|
|
;;; Single DA conversion. The 2 bytes are in the FIFO buffer
|
|
single_da:
|
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT
|
|
lcall dalo ; conversion
|
|
sjmp over_da
|
|
|
|
;;; configure the port B as input or output (bitwise)
|
|
config_digital_b:
|
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT
|
|
movx a,@dptr ; get the second byte
|
|
inc dptr
|
|
mov OEB,a ; set the output enable bits
|
|
movx a,@dptr ; get the second byte
|
|
inc dptr
|
|
mov OEC,a
|
|
movx a,@dptr ; get the second byte
|
|
inc dptr
|
|
mov OED,a
|
|
sjmp over_da
|
|
|
|
;;; Write one byte to the external digital port B
|
|
;;; and prepare for digital read
|
|
write_digital_b:
|
|
mov dptr,#0e781h ; FIFO buffer of EP1OUT
|
|
movx a,@dptr ; command[1]
|
|
inc dptr
|
|
mov OEB,a ; output enable
|
|
movx a,@dptr ; command[2]
|
|
inc dptr
|
|
mov OEC,a
|
|
movx a,@dptr ; command[3]
|
|
inc dptr
|
|
mov OED,a
|
|
movx a,@dptr ; command[4]
|
|
inc dptr
|
|
mov IOB,a ;
|
|
movx a,@dptr ; command[5]
|
|
inc dptr
|
|
mov IOC,a
|
|
movx a,@dptr ; command[6]
|
|
inc dptr
|
|
mov IOD,a
|
|
|
|
lcall reset_ep8 ; reset FIFO of ep 8
|
|
|
|
;; fill ep8 with new data from port B
|
|
;; When the host requests the data it's already there.
|
|
;; This must be so. Otherwise the ISR is not called.
|
|
;; The ISR is only called when a packet has been delivered
|
|
;; to the host. Thus, we need a packet here in the
|
|
;; first instance.
|
|
lcall ep8_ops ; get digital data
|
|
|
|
;;
|
|
;; for all commands the same
|
|
over_da:
|
|
mov dptr,#EP1OUTBC
|
|
mov a,#00h
|
|
lcall syncdelaywr ; arm
|
|
lcall syncdelaywr ; arm
|
|
lcall syncdelaywr ; arm
|
|
|
|
;; clear INT2
|
|
mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
|
|
clr acc.4
|
|
mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
|
|
|
|
mov DPTR,#EPIRQ ;
|
|
mov a,#00001000b ; clear the ep1outirq
|
|
movx @DPTR,a
|
|
|
|
pop 07h
|
|
pop 06h
|
|
pop 05h
|
|
pop 04h ; R4
|
|
pop 03h ; R3
|
|
pop 02h ; R2
|
|
pop 01h ; R1
|
|
pop 00h ; R0
|
|
pop psw
|
|
pop acc
|
|
pop dph1
|
|
pop dpl1
|
|
pop dph
|
|
pop dpl
|
|
pop dps
|
|
reti
|
|
|
|
|
|
|
|
;;; save all DA channels from the endpoint buffer in a local buffer
|
|
dalo:
|
|
movx a,@dptr ; number of bytes to send out
|
|
inc dptr ; pointer to the first byte
|
|
mov r1,#DABUFFER ; buffer for DA values
|
|
mov @r1,a ; save it
|
|
inc r1 ; inc pointer to local buffer
|
|
mov r0,a ; counter
|
|
nextDAlo:
|
|
movx a,@dptr ; get the byte
|
|
inc dptr ; point to the high byte
|
|
mov @r1,a ; save it in the buffer
|
|
inc r1
|
|
movx a,@dptr ; get the channel number
|
|
inc dptr ; get ready for the next channel
|
|
mov @r1,a ; save it
|
|
inc r1
|
|
djnz r0,nextDAlo ; next channel
|
|
ret
|
|
|
|
|
|
;;; write to the DA converter
|
|
DAsend:
|
|
mov r1,#DABUFFER ; buffer of the DA values
|
|
mov a,@r1 ; get the channel count
|
|
jz DAret ; nothing to do
|
|
inc r1 ; pointer to the first byte
|
|
mov r0,a ; counter
|
|
nextDA:
|
|
mov a,@r1 ; get the byte
|
|
inc r1 ; point to the high byte
|
|
mov r3,a ; store in r3 for writeDA
|
|
mov a,@r1 ; get the channel number
|
|
inc r1 ; get ready for the next channel
|
|
push 1 ; is modified in the subroutine
|
|
lcall writeDA ; write value to the DAC
|
|
pop 1 ; get the pointer back
|
|
djnz r0,nextDA ; next channel
|
|
DAret:
|
|
ret
|
|
|
|
|
|
|
|
;;; D/A-conversion:
|
|
;;; channel number in a
|
|
;;; value in r3
|
|
writeDA:
|
|
anl a,#00000011b ; 4 channels
|
|
mov r1,#6 ; the channel number needs to be shifted up
|
|
writeDA2:
|
|
rl a ; bit shift to the left
|
|
djnz r1,writeDA2 ; do it 6 times
|
|
orl a,#00010000b ; update outputs after write
|
|
mov r2,a ; backup
|
|
mov a,r3 ; get byte
|
|
anl a,#11110000b ; get the upper nibble
|
|
mov r1,#4 ; shift it up to the upper nibble
|
|
writeDA3:
|
|
rr a ; shift to the upper to the lower
|
|
djnz r1,writeDA3
|
|
orl a,r2 ; merge with the channel info
|
|
clr IOA.6 ; /SYNC (/CS) of the DA to 0
|
|
lcall sendSPI ; send it out to the SPI
|
|
mov a,r3 ; get data again
|
|
anl a,#00001111b ; get the lower nibble
|
|
mov r1,#4 ; shift that to the upper
|
|
writeDA4:
|
|
rl a
|
|
djnz r1,writeDA4
|
|
anl a,#11110000b ; make sure that's empty
|
|
lcall sendSPI
|
|
setb IOA.6 ; /SYNC (/CS) of the DA to 1
|
|
noDA: ret
|
|
|
|
|
|
|
|
;;; arm ep6: this is just a dummy arm to get things going
|
|
ep6_arm:
|
|
mov DPTR,#EP6BCH ; byte count H
|
|
mov a,#0 ; is zero
|
|
lcall syncdelaywr ; wait until the length has arrived
|
|
|
|
mov DPTR,#EP6BCL ; byte count L
|
|
mov a,#1 ; is one
|
|
lcall syncdelaywr ; wait until the length has been proc
|
|
ret
|
|
|
|
|
|
|
|
;;; converts one analog/digital channel and stores it in EP8
|
|
;;; also gets the content of the digital ports B,C and D depending on
|
|
;;; the COMMAND flag
|
|
ep8_ops:
|
|
mov dptr,#0fc01h ; ep8 fifo buffer
|
|
clr a ; high byte
|
|
movx @dptr,a ; set H=0
|
|
mov dptr,#0fc00h ; low byte
|
|
mov r0,#CMD_FLAG
|
|
mov a,@r0
|
|
movx @dptr,a ; save command byte
|
|
|
|
mov dptr,#ep8_jmp ; jump table for the different functions
|
|
rl a ; multiply by 2: sizeof sjmp
|
|
jmp @a+dptr ; jump to the jump table
|
|
;; jump table, corresponds to the command bytes defined
|
|
;; in usbdux.c
|
|
ep8_jmp:
|
|
sjmp ep8_err ; a=0, err
|
|
sjmp ep8_err ; a=1, err
|
|
sjmp ep8_err ; a=2, err
|
|
sjmp ep8_dio ; a=3, digital read
|
|
sjmp ep8_sglchannel ; a=4, analog A/D
|
|
sjmp ep8_err ; a=5, err
|
|
sjmp ep8_err ; a=6, err
|
|
|
|
;; read one A/D channel
|
|
ep8_sglchannel:
|
|
setb IOA.7 ; start converter, START = 1
|
|
;; we do polling: we wait until DATA READY is zero
|
|
sglchwait:
|
|
mov a,IOA ; get /DRDY
|
|
jb ACC.0,sglchwait ; wait until data ready (DRDY=0)
|
|
mov DPTR,#0fc01h ; EP8 FIFO
|
|
lcall readADCch ; get one reading
|
|
clr IOA.7 ; stop the converter, START = 0
|
|
|
|
sjmp ep8_send ; send the data
|
|
|
|
;; read the digital lines
|
|
ep8_dio:
|
|
mov DPTR,#0fc01h ; store the contents of port B
|
|
mov a,IOB ; in the next
|
|
movx @dptr,a ; entry of the buffer
|
|
inc dptr
|
|
mov a,IOC ; port C
|
|
movx @dptr,a ; next byte of the EP
|
|
inc dptr
|
|
mov a,IOD
|
|
movx @dptr,a ; port D
|
|
|
|
ep8_send:
|
|
mov DPTR,#EP8BCH ; byte count H
|
|
mov a,#0 ; is zero
|
|
lcall syncdelaywr
|
|
|
|
mov DPTR,#EP8BCL ; byte count L
|
|
mov a,#10H ; 16 bytes, bec it's such a great number...
|
|
lcall syncdelaywr ; send the data over to the host
|
|
|
|
ep8_err:
|
|
ret
|
|
|
|
|
|
|
|
;;; EP8 interrupt is the endpoint which sends data back after a command
|
|
;;; The actual command fills the EP buffer already
|
|
;;; but for INSNs we need to deliver more data if the count > 1
|
|
ep8_isr:
|
|
push dps
|
|
push dpl
|
|
push dph
|
|
push dpl1
|
|
push dph1
|
|
push acc
|
|
push psw
|
|
push 00h ; R0
|
|
push 01h ; R1
|
|
push 02h ; R2
|
|
push 03h ; R3
|
|
push 04h ; R4
|
|
push 05h ; R5
|
|
push 06h ; R6
|
|
push 07h ; R7
|
|
|
|
lcall ep8_ops
|
|
|
|
;; clear INT2
|
|
mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
|
|
clr acc.4
|
|
mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable
|
|
|
|
mov DPTR,#EPIRQ ;
|
|
mov a,#10000000b ; clear the ep8irq
|
|
movx @DPTR,a
|
|
|
|
pop 07h
|
|
pop 06h
|
|
pop 05h
|
|
pop 04h ; R4
|
|
pop 03h ; R3
|
|
pop 02h ; R2
|
|
pop 01h ; R1
|
|
pop 00h ; R0
|
|
pop psw
|
|
pop acc
|
|
pop dph1
|
|
pop dpl1
|
|
pop dph
|
|
pop dpl
|
|
pop dps
|
|
reti
|
|
|
|
|
|
|
|
;;; GPIF waveform for PWM
|
|
waveform:
|
|
;; 0 1 2 3 4 5 6 7(not used)
|
|
;; len (gives 50.007Hz)
|
|
.db 195, 195, 195, 195, 195, 195, 1, 1
|
|
|
|
;; opcode
|
|
.db 002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H
|
|
|
|
;; out
|
|
.db 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH
|
|
|
|
;; log
|
|
.db 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H
|
|
|
|
|
|
stopPWM:
|
|
mov r0,#PWMFLAG ; flag for PWM
|
|
mov a,#0 ; PWM (for the main loop)
|
|
mov @r0,a ; set it
|
|
|
|
mov dptr,#IFCONFIG ; switch off GPIF
|
|
mov a,#10100000b ; gpif, 30MHz, internal IFCLK
|
|
lcall syncdelaywr
|
|
ret
|
|
|
|
|
|
;;; init PWM
|
|
startPWM:
|
|
mov dptr,#IFCONFIG ; switch on IFCLK signal
|
|
mov a,#10100010b ; gpif, 30MHz, internal IFCLK
|
|
lcall syncdelaywr
|
|
|
|
mov OEB,0FFH ; output to port B
|
|
|
|
mov DPTR,#EP4CFG
|
|
mov a,#10100000b ; valid, out, bulk
|
|
movx @DPTR,a
|
|
|
|
;; reset the endpoint
|
|
mov dptr,#FIFORESET
|
|
mov a,#80h ; NAK
|
|
lcall syncdelaywr
|
|
mov a,#84h ; reset EP4 + NAK
|
|
lcall syncdelaywr
|
|
mov a,#0 ; normal op
|
|
lcall syncdelaywr
|
|
|
|
mov dptr,#EP4BCL
|
|
mov a,#0H ; discard packets
|
|
lcall syncdelaywr ; empty FIFO buffer
|
|
lcall syncdelaywr ; empty FIFO buffer
|
|
|
|
;; aborts all transfers by the GPIF
|
|
mov dptr,#GPIFABORT
|
|
mov a,#0ffh ; abort all transfers
|
|
lcall syncdelaywr
|
|
|
|
;; wait for GPIF to finish
|
|
wait_f_abort:
|
|
mov a,GPIFTRIG ; GPIF status
|
|
anl a,#80h ; done bit
|
|
jz wait_f_abort ; GPIF busy
|
|
|
|
mov dptr,#GPIFCTLCFG
|
|
mov a,#10000000b ; tri state for CTRL
|
|
lcall syncdelaywr
|
|
|
|
mov dptr,#GPIFIDLECTL
|
|
mov a,#11110000b ; all CTL outputs low
|
|
lcall syncdelaywr
|
|
|
|
;; abort if FIFO is empty
|
|
mov a,#00000001b ; abort if empty
|
|
mov dptr,#EP4GPIFFLGSEL
|
|
lcall syncdelaywr
|
|
|
|
;;
|
|
mov a,#00000001b ; stop if GPIF flg
|
|
mov dptr,#EP4GPIFPFSTOP
|
|
lcall syncdelaywr
|
|
|
|
;; transaction counter
|
|
mov a,#0ffH
|
|
mov dptr,#GPIFTCB3
|
|
lcall syncdelaywr
|
|
|
|
;; transaction counter
|
|
mov a,#0ffH
|
|
mov dptr,#GPIFTCB2
|
|
lcall syncdelaywr
|
|
|
|
;; transaction counter
|
|
mov a,#0ffH ; 512 bytes
|
|
mov dptr,#GPIFTCB1
|
|
lcall syncdelaywr
|
|
|
|
;; transaction counter
|
|
mov a,#0ffH
|
|
mov dptr,#GPIFTCB0
|
|
lcall syncdelaywr
|
|
|
|
;; RDY pins. Not used here.
|
|
mov a,#0
|
|
mov dptr,#GPIFREADYCFG
|
|
lcall syncdelaywr
|
|
|
|
;; drives the output in the IDLE state
|
|
mov a,#1
|
|
mov dptr,#GPIFIDLECS
|
|
lcall syncdelaywr
|
|
|
|
;; direct data transfer from the EP to the GPIF
|
|
mov dptr,#EP4FIFOCFG
|
|
mov a,#00010000b ; autoout=1, byte-wide
|
|
lcall syncdelaywr
|
|
|
|
;; waveform 0 is used for FIFO out
|
|
mov dptr,#GPIFWFSELECT
|
|
mov a,#00000000b
|
|
movx @dptr,a
|
|
lcall syncdelay
|
|
|
|
;; transfer the delay byte from the EP to the waveform
|
|
mov dptr,#0e781h ; EP1 buffer
|
|
movx a,@dptr ; get the delay
|
|
mov dptr,#waveform ; points to the waveform
|
|
mov r2,#6 ; fill 6 bytes
|
|
timloop:
|
|
movx @dptr,a ; save timing in a xxx
|
|
inc dptr
|
|
djnz r2,timloop ; fill the 6 delay bytes
|
|
|
|
;; load waveform
|
|
mov AUTOPTRH2,#0E4H ; XDATA0H
|
|
lcall syncdelay
|
|
mov AUTOPTRL2,#00H ; XDATA0L
|
|
lcall syncdelay
|
|
|
|
mov dptr,#waveform ; points to the waveform
|
|
|
|
mov AUTOPTRSETUP,#7 ; autoinc and enable
|
|
lcall syncdelay
|
|
|
|
mov r2,#20H ; 32 bytes to transfer
|
|
|
|
wavetr:
|
|
movx a,@dptr
|
|
inc dptr
|
|
push dpl
|
|
push dph
|
|
push dpl1
|
|
push dph1
|
|
mov dptr,#XAUTODAT2
|
|
movx @dptr,a
|
|
lcall syncdelay
|
|
pop dph1
|
|
pop dpl1
|
|
pop dph
|
|
pop dpl
|
|
djnz r2,wavetr
|
|
|
|
mov dptr,#OUTPKTEND
|
|
mov a,#084H
|
|
lcall syncdelaywr
|
|
lcall syncdelaywr
|
|
|
|
mov r0,#PWMFLAG ; flag for PWM
|
|
mov a,#1 ; PWM (for the main loop)
|
|
mov @r0,a ; set it
|
|
|
|
ret
|
|
|
|
|
|
|
|
;; need to delay every time the byte counters
|
|
;; for the EPs have been changed.
|
|
|
|
syncdelay:
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
ret
|
|
|
|
syncdelaywr:
|
|
movx @dptr,a
|
|
lcall syncdelay
|
|
ret
|
|
|
|
|
|
|
|
.org 1F00h ; lookup table at the end of memory
|
|
|
|
swap_lut:
|
|
.db 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136
|
|
.db 72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100
|
|
.db 228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220
|
|
.db 60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10
|
|
.db 138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166
|
|
.db 102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94
|
|
.db 222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9
|
|
.db 137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165
|
|
.db 101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93
|
|
.db 221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11
|
|
.db 139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167
|
|
.db 103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95
|
|
.db 223,63,191,127,255
|
|
|
|
|
|
|
|
|
|
.End
|
|
|
|
|