This commit is contained in:
drohne235 2010-03-18 18:59:13 +00:00
parent 0e2ad4b175
commit 1009b1cf3f
5 changed files with 1617 additions and 0 deletions

View File

@ -0,0 +1,711 @@
{{
' fsrw.spin 1.6 Copyright 2008 Radical Eye Software
'
' See end of file for terms of use.
'
' This object provides FAT16 file read/write access on a block device.
' Only one file open at a time. Open modes are 'r' (read), 'a' (append),
' 'w' (write), and 'd' (delete). Only the root directory is supported.
' No long filenames are supported. We also support traversing the
' root directory.
'
' In general, negative return values are errors; positive return
' values are success. Other than -1 on popen when the file does not
' exist, all negative return values will be "aborted" rather than
' returned.
'
' Changes:
' v1.1 28 December 2006 Fixed offset for ctime
' v1.2 29 December 2006 Made default block driver be fast one
' v1.3 6 January 2007 Added some docs, and a faster asm
' v1.4 4 February 2007 Rearranged vars to save memory;
' eliminated need for adjacent pins;
' reduced idle current consumption; added
' sample code with abort code data
' v1.5 7 April 2007 Fixed problem when directory is larger
' than a cluster.
' v1.6 23 September 2008 Fixed a bug found when mixing pputc
' with pwrite. Also made the assembly
' routines a bit more cautious.
}}
'
' Constants describing FAT volumes.
'
con
SECTORSIZE = 512
SECTORSHIFT = 9
DIRSIZE = 32
DIRSHIFT = 5
'
' The object that provides the block-level access.
'
obj
sdspi: "admflash-sdspiqasm"
var
'
'
' Variables concerning the open file.
'
long fclust ' the current cluster number
long filesize ' the total current size of the file
long floc ' the seek position of the file
long frem ' how many bytes remain in this cluster from this file
long bufat ' where in the buffer our current character is
long bufend ' the last valid character (read) or free position (write)
long direntry ' the byte address of the directory entry (if open for write)
long writelink ' the byte offset of the disk location to store a new cluster
long fatptr ' the byte address of the most recently written fat entry
long fsize ' dateigröße
long fattrib ' dateiattribute
long ftime ' zeitstempel
'
' Variables used when mounting to describe the FAT layout of the card.
'
long rootdir ' the byte address of the start of the root directory
long rootdirend ' the byte immediately following the root directory.
long dataregion ' the start of the data region, offset by two sectors
long clustershift ' log base 2 of blocks per cluster
long fat1 ' the block address of the fat1 space
long totclusters ' how many clusters in the volume
long sectorsperfat ' how many sectors per fat
'
' Variables controlling the caching.
'
long lastread ' the block address of the buf2 contents
long dirty ' nonzero if buf2 is dirty
'
' Buffering: two sector buffers. These two buffers must be longword
' aligned! To ensure this, make sure they are the first byte variables
' defined in this object.
'
byte buf[SECTORSIZE] ' main data buffer
byte buf2[SECTORSIZE] ' main metadata buffer
byte padname[11] ' filename buffer
CON { SEKTORINTERFACE
das sektorinterface arbeitet mit zusammenhängenden containerdateien und ermöglicht einen wahlfreien
zugriff auf die daten. die containerdatei werden mit dem entsprechenden tool auf einer leeren und
frisch formatierten karte erzeugt.
}
PUB sec_start(strptr): s | err 'sec: startsektor einer containerdatei ermitteln
{{ conainerdatei wird geöffnet und startsektor berechnet. die sektornummer wird zurückgeliefert;
bei einem fehler der wert -1}}
s~
err := popen(strptr,"r") 'containerdatei öffnen
ifnot err 'kein fehler bei öffnen des containers
s := (fclust << clustershift) + dataregion 'startsektor berechnen
pclose 'containerdatei schließen
return s
else
return -1
' sequenz aus pfillbuf:
' sdspi.readblock(datablock, @buf) 'cluster einlesen
' sequenz aus datablock:
' return (fclust << clustershift) + dataregion + ((floc >> SECTORSHIFT) & ((1 << clustershift) - 1))
PUB sec_readblock(secnr, badr)
sdspi.readblock(secnr, badr)
PUB sec_writeblock(secnr, badr)
sdspi.writeblock(secnr, badr)
CON { FAT16-Code
}
pri writeblock2(n, b)
'
' On metadata writes, if we are updating the FAT region, also update
' the second FAT region.
'
sdspi.writeblock(n, b)
if (n => fat1 and n < fat1 + sectorsperfat)
sdspi.writeblock(n+sectorsperfat, b)
pri flushifdirty
'
' If the metadata block is dirty, write it out.
'
if (dirty)
writeblock2(lastread, @buf2)
dirty := 0
pri readblockc(n)
'
' Read a block into the metadata buffer, if that block is not already
' there.
'
if (n <> lastread)
flushifdirty
sdspi.readblock(n, @buf2)
lastread := n
pri brword(b)
'
' Read a byte-reversed word from a (possibly odd) address.
'
return (byte[b]) + ((byte[b][1]) << 8)
pri brlong(b)
'
' Read a byte-reversed long from a (possibly odd) address.
'
return brword(b) + (brword(b+2) << 16)
pri brwword(w, v)
'
' Write a byte-reversed word to a (possibly odd) address, and
' mark the metadata buffer as dirty.
'
byte[w++] := v
byte[w] := v >> 8
dirty := 1
pri brwlong(w, v)
'
' Write a byte-reversed long to a (possibly odd) address, and
' mark the metadata buffer as dirty.
'
brwword(w, v)
brwword(w+2, v >> 16)
pub mount(basepin) | start, sectorspercluster, reserved, rootentries, sectors
{{
' Mount a volume. The address passed in is passed along to the block
' layer; see the currently used block layer for documentation. If the
' volume mounts, a 0 is returned, else abort is called.
}}
sdspi.start(basepin)
lastread := -1
dirty := 0
sdspi.readblock(0, @buf)
if (brlong(@buf+$36) == constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)))
start := 0
else
start := brlong(@buf+$1c6)
sdspi.readblock(start, @buf)
if (brlong(@buf+$36) <> constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)) or buf[$3a] <> "6")
return 1 ' not a fat16 volume
if (brword(@buf+$0b) <> SECTORSIZE)
return 2 ' bad bytes per sector
sectorspercluster := buf[$0d]
if (sectorspercluster & (sectorspercluster - 1))
return 3 ' bad sectors per cluster
clustershift := 0
repeat while (sectorspercluster > 1)
clustershift++
sectorspercluster >>= 1
sectorspercluster := 1 << clustershift
reserved := brword(@buf+$0e)
if (buf[$10] <> 2)
return 4 ' not two FATs
rootentries := brword(@buf+$11)
sectors := brword(@buf+$13)
if (sectors == 0)
sectors := brlong(@buf+$20)
sectorsperfat := brword(@buf+$16)
if (brword(@buf+$1fe) <> $aa55)
return 5 ' bad FAT signature
fat1 := start + reserved
rootdir := (fat1 + 2 * sectorsperfat) << SECTORSHIFT
rootdirend := rootdir + (rootentries << DIRSHIFT)
dataregion := 1 + ((rootdirend - 1) >> SECTORSHIFT) - 2 * sectorspercluster
totclusters := ((sectors - dataregion + start) >> clustershift)
if (totclusters > $fff0)
return 6 ' too many clusters
return 0
pri readbytec(byteloc)
'
' Read a byte address from the disk through the metadata buffer and
' return a pointer to that location.
'
readblockc(byteloc >> SECTORSHIFT)
return @buf2 + (byteloc & constant(SECTORSIZE - 1))
pri readfat(clust)
'
' Read a fat location and return a pointer to the location of that
' entry.
'
fatptr := (fat1 << SECTORSHIFT) + (clust << 1)
return readbytec(fatptr)
pri followchain | clust
'
' Follow the fat chain and update the writelink.
'
clust := brword(readfat(fclust))
writelink := fatptr
return clust
pri nextcluster | clust
'
' Read the next cluster and return it. Set up writelink to
' point to the cluster we just read, for later updating. If the
' cluster number is bad, return a negative number.
'
clust := followchain
if (clust < 2 or clust => totclusters)
abort(-9) ' bad cluster value
return clust
pri freeclusters(clust) | bp
'
' Free an entire cluster chain. Used by remove and by overwrite.
' Assumes the pointer has already been cleared/set to $ffff.
'
repeat while (clust < $fff0)
if (clust < 2)
abort(-26) ' bad cluster number")
bp := readfat(clust)
clust := brword(bp)
brwword(bp, 0)
flushifdirty
pri datablock
'
' Calculate the block address of the current data location.
'
return (fclust << clustershift) + dataregion + ((floc >> SECTORSHIFT) & ((1 << clustershift) - 1))
pri uc(c)
'
' Compute the upper case version of a character.
'
if ("a" =< c and c =< "z")
return c - 32
return c
pri pflushbuf(r, metadata) | cluststart, newcluster, count, i
'
' Flush the current buffer, if we are open for write. This may
' allocate a new cluster if needed. If metadata is true, the
' metadata is written through to disk including any FAT cluster
' allocations and also the file size in the directory entry.
'
if (direntry == 0)
abort(-27) ' not open for writing
if (r > 0) ' must *not* allocate cluster if flushing an empty buffer
if (frem < SECTORSIZE)
' find a new clustercould be anywhere! If possible, stay on the
' same page used for the last cluster.
newcluster := -1
cluststart := fclust & constant(!((SECTORSIZE >> 1) - 1))
count := 2
repeat
readfat(cluststart)
repeat i from 0 to constant(SECTORSIZE - 2) step 2
if (buf2[i]==0 and buf2[i+1]==0)
newcluster := cluststart + (i >> 1)
if (newcluster => totclusters)
newcluster := -1
quit
if (newcluster > 1)
brwword(@buf2+i, -1)
brwword(readbytec(writelink), newcluster)
writelink := fatptr + i
fclust := newcluster
frem := SECTORSIZE << clustershift
quit
else
cluststart += constant(SECTORSIZE >> 1)
if (cluststart => totclusters)
cluststart := 0
count--
if (count < 0)
r := -5 ' No space left on device
quit
if (frem => SECTORSIZE)
sdspi.writeblock(datablock, @buf)
if (r == SECTORSIZE) ' full buffer, clear it
floc += r
frem -= r
bufat := 0
bufend := r
else
' not a full blockleave pointers alone
if (r < 0 or metadata) ' update metadata even if error
readblockc(direntry >> SECTORSHIFT) ' flushes unwritten FAT too
brwlong(@buf2+(direntry & constant(SECTORSIZE-1))+28, floc+bufat)
flushifdirty
if (r < 0)
abort(r)
return r
pub pflush
{{
' Call flush with the current data buffer location, and the flush
' metadata flag set.
}}
return pflushbuf(bufat, 1)
pri pfillbuf | r
'
' Get some data into an empty buffer. If no more data is available,
' return -1. Otherwise return the number of bytes read into the
' buffer.
'
if (floc => filesize)
return -1
if (frem == 0)
fclust := nextcluster 'nächster cluster
frem := SECTORSIZE << clustershift
if (frem + floc > filesize)
frem := filesize - floc
sdspi.readblock(datablock, @buf) 'cluster einlesen
r := SECTORSIZE
if (floc + r => filesize)
r := filesize - floc
floc += r
frem -= r
bufat := 0
bufend := r
return r
pub pclose | r
{{
' Flush and close the currently open file if any. Also reset the
' pointers to valid values. If there is no error, 0 will be returned.
}}
r := 0
if (direntry)
r := pflush
bufat := 0
bufend := 0
filesize := 0
floc := 0
frem := 0
writelink := 0
direntry := 0
fclust := 0
return r
pri pdate
{{
' Get the current date and time, as a long, in the format required
' by FAT16. Right now it"s hardwired to return the date this
' software was created on (April 7, 2007). You can change this
' to return a valid date/time if you have access to this data in
' your setup.
}}
return constant(((2007-1980) << 25) + (1 << 21) + (7 << 16) + (4 << 11))
pub popen(s, mode) | i, sentinel, dirptr, freeentry
{{
' Close any currently open file, and open a new one with the given
' file name and mode. Mode can be "r" "w" "a" or "d" (delete).
' If the file is opened successfully, 0 will be returned. If the
' file did not exist, and the mode was not "w" or "a", -1 will be
' returned. Otherwise abort will be called with a negative error
' code.
'
' s - zeiger auf string mit dateinamen
' mode - r, w, a, d
}}
pclose
' ----------------------------------------------------------- dateinamen aufbereiten
i := 0
repeat while (i<8 and byte[s] and byte[s] <> ".") 'kopiert dateinamen ohne extender
padname[i++] := uc(byte[s++]) 'uc wandelt in kleinbuchstaben
repeat while (i<8)
padname[i++] := " "
repeat while (byte[s] and byte[s] <> ".")
s++
if (byte[s] == ".")
s++
repeat while (i<11 and byte[s])
padname[i++] := uc(byte[s++])
repeat while (i < 11)
padname[i++] := " "
' ----------------------------------------------------------- datei im verzeichnis suchen
sentinel := 0
freeentry := 0
repeat dirptr from rootdir to rootdirend - DIRSIZE step DIRSIZE
s := readbytec(dirptr)
if (freeentry == 0 and (byte[s] == 0 or byte[s] == $e5))
freeentry := dirptr
if (byte[s] == 0)
sentinel := dirptr
quit
repeat i from 0 to 10 'vergleicht eintrag mit dateinamen
if (padname[i] <> byte[s][i]) 'bei gleichheit i == 11
quit
if (i == 11 and 0 == (byte[s][$0b] & $18)) 'dateiname gefunden und kein verzeichnis/volume
fclust := brword(s+$1a) 'startcluster der datei lesen und als aktuell setzen
filesize := brlong(s+$1c) 'dateigröße der datei lesen
fsize := filesize 'dateigröße der datei lesen
if (mode == "r") 'DATEI LESEN
frem := SECTORSIZE << clustershift
if (frem > filesize)
frem := filesize
return 0
if (byte[s][11] & $d9) ' datei ist schreibgeschützt
abort(-6) ' no permission to write
if (mode == "d") 'DATEI LÖSCHEN
brwword(s, $e5) 'verzeichniseintrag als gelöscht markieren
freeclusters(fclust) 'cluster freigeben
flushifdirty
return 0
if (mode == "w") 'DATEI SCHREIBEN/ÜBERSCHREIBEN
brwword(s+26, -1) 'bestehende clusterreferenz ungültig machen
brwlong(s+28, 0) 'größe der neuen datei auf 0 setzen
writelink := dirptr + 26
direntry := dirptr
freeclusters(fclust) 'bestehende clusterkette freigeben
bufend := SECTORSIZE
fclust := 0
filesize := 0
frem := 0
return 0
elseif (mode == "a") 'DATEI ANHÄNGEN
' this code will eventually be moved to seek
frem := filesize
freeentry := SECTORSIZE << clustershift 'freeentry = clustergröße in bytes
if (fclust => $fff0)
fclust := 0
repeat while (frem > freeentry) 'bis zum letzten cluster springen
if (fclust < 2)
abort(-7) ' eof repeat while following chain
fclust := nextcluster 'springe zum nächsten cluster
frem -= freeentry 'berechne neue größe bis dateiende
'in frem bleiben anzahl bytes im letzten cluster
floc := filesize & constant(!(SECTORSIZE - 1)) 'sektornummer dateiende berechnen
bufend := SECTORSIZE
bufat := frem & constant(SECTORSIZE - 1)
writelink := dirptr + 26
direntry := dirptr
if (bufat)
sdspi.readblock(datablock, @buf)
frem := freeentry - (floc & (freeentry - 1))
else
if (fclust < 2 or frem == freeentry)
frem := 0
else
frem := freeentry - (floc & (freeentry - 1))
if (fclust => 2)
followchain
return 0
else
abort(-3) ' bad argument
' ----------------------------------------------------------- datei nicht gefunden, neue datei erzeugen
if (mode <> "w" and mode <> "a")
return -1 ' not found
direntry := freeentry
if (direntry == 0)
abort(-2) ' no empty directory entry
' write (or new append): create valid directory entry
s := readbytec(direntry)
bytefill(s, 0, DIRSIZE)
bytemove(s, @padname, 11)
brwword(s+26, -1)
i := pdate
brwlong(s+$e, i) ' write create time and date
brwlong(s+$16, i) ' write last modified date and time
if (direntry == sentinel and direntry + DIRSIZE < rootdirend)
brwword(readbytec(direntry+DIRSIZE), 0)
flushifdirty
writelink := direntry + 26
fclust := 0
bufend := SECTORSIZE
return 0
pub pseek(bytenr) | freeentry,dirptr 'setzt zeiger auf position
{{
- springt erst alle ganzen cluster weiter
- restbytes im letzten cluster werden geladen
- code funktioniert nicht!!!
}}
{{
frem := bytenr
freeentry := SECTORSIZE << clustershift 'freeentry = clustergröße in bytes
if (fclust => $fff0)
fclust := 0
repeat while (frem > freeentry) 'bis zum cluster springen
if (fclust < 2)
abort(-7) 'eof repeat while following chain
fclust := nextcluster 'springe zum nächsten cluster
frem -= freeentry 'berechne neue größe bis dateiende
'in frem bleiben anzahl bytes im letzten cluster
floc := frem & constant(!(SECTORSIZE - 1)) 'sektornummer berechnen
bufend := SECTORSIZE
bufat := frem & constant(SECTORSIZE - 1) 'restbytes über clustergrenze berechnen
writelink := dirptr + 26
direntry := dirptr
if (bufat)
sdspi.readblock(datablock, @buf) 'restbytes einlesen in buf
frem := freeentry - (floc & (freeentry - 1))
else
if (fclust < 2 or frem == freeentry)
frem := 0
else
frem := freeentry - (floc & (freeentry - 1))
if (fclust => 2)
followchain
return 0
}}
pub pread(ubuf, count) | r, t
{{
' Read count bytes into the buffer ubuf. Returns the number of bytes
' successfully read, or a negative number if there is an error.
' The buffer may be as large as you want.
}}
r := 0
repeat while (count > 0)
if (bufat => bufend)
t := pfillbuf
if (t =< 0)
if (r > 0)
return r
return t
t := bufend - bufat
if (t > count)
t := count
bytemove(ubuf, @buf+bufat, t)
bufat += t
r += t
ubuf += t
count -= t
return r
pub pgetc | t
{{
' Read and return a single character. If the end of file is
' reached, -1 will be returned. If an error occurs, a negative
' number will be returned.
}}
if (bufat => bufend)
t := pfillbuf
if (t =< 0)
return -1
return (buf[bufat++])
pub pwrite(ubuf, count) | r, t
{{
' Write count bytes from the buffer ubuf. Returns the number of bytes
' successfully written, or a negative number if there is an error.
' The buffer may be as large as you want.
}}
t := 0
repeat while (count > 0)
if (bufat => bufend)
t := pflushbuf(bufat, 0)
t := bufend - bufat
if (t > count)
t := count
bytemove(@buf+bufat, ubuf, t)
r += t
bufat += t
ubuf += t
count -= t
return t
pub pputc(c)
{{
' Write a single character into the file open for write. Returns
' 0 if successful, or a negative number if some error occurred.
}}
if (bufat == SECTORSIZE) 'ist sectorende erreicht?
pflushbuf(SECTORSIZE, 0)
buf[bufat++] := c
return 0
pub opendir | off
{{
' Close the currently open file, and set up the read buffer for
' calls to nextfile.
}}
pclose
off := rootdir - (dataregion << SECTORSHIFT)
fclust := off >> (clustershift + SECTORSHIFT)
floc := off - (fclust << (clustershift + SECTORSHIFT))
frem := rootdirend - rootdir
filesize := floc + frem
return 0
pub nextfile(fbuf) | i, t, at, lns
{{
' Find the next file in the root directory and extract its
' (8.3) name into fbuf. Fbuf must be sized to hold at least
' 13 characters (8 + 1 + 3 + 1). If there is no next file,
' -1 will be returned. If there is, 0 will be returned.
}}
repeat
if (bufat => bufend)
t := pfillbuf
if (t < 0)
return t
if (((floc >> SECTORSHIFT) & ((1 << clustershift) - 1)) == 0)
fclust++
at := @buf + bufat
if (byte[at] == 0) 'verzeichnisende erreicht
return -1
bufat += DIRSIZE
if (byte[at] <> $e5 and (byte[at][$0b] & $18) == 0)
fsize := brlong(at+$1c) 'dateigröße der datei lesen
fattrib := byte[at][$0b] 'attribute setzen
ftime := brlong(at+$0e) 'zeitstempel setzen
lns := fbuf
repeat i from 0 to 10
byte[fbuf] := byte[at][i]
fbuf++
if (byte[at][i] <> " ")
lns := fbuf
if (i == 7 or i == 10)
fbuf := lns
if (i == 7)
byte[fbuf] := "."
fbuf++
byte[fbuf] := 0
return 0
pub getfsize 'dateigröße übergeben
return fsize
pub getfattrib 'attribute übergeben
return fattrib
pub getftime 'zeitstempel übergeben
return ftime
{{
' Permission is hereby granted, free of charge, to any person obtaining
' a copy of this software and associated documentation files
' (the "Software"), to deal in the Software without restriction,
' including without limitation the rights to use, copy, modify, merge,
' publish, distribute, sublicense, and/or sell copies of the Software,
' and to permit persons to whom the Software is furnished to do so,
' subject to the following conditions:
'
' The above copyright notice and this permission notice shall be included
' in all copies or substantial portions of the Software.
'
' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
' EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
' MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
' IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
' CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
' TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
' SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}}

View File

@ -0,0 +1,741 @@
''*****************************
''* Hydra Sound System v1.2 *
''* (C)2007 Andrew Arsenault *
''*****************************
''http://www.andrewarsenault.com/hss/
''e-mail: ym2413a@yahoo.com
''
'' Cogs used: 2
'' HUB-RAM: ~2.7k
'' Please visit the website for the latest version, documentation, examples and media files.
'' Thank you! --Ym2413a
'' 25.01.2009 Anpassungen für ein komfortableres Interface zur Visualisierung und Steuerung
CON
#0, iEndFlag 'Repeat oder Ende wurde erreicht
iRowFlag 'Flag das Songzeile fertig ist
iEngineC 'Patternzähler
iBeatC 'Beatzähler
iRepeat 'zähler für loops
#5, iChannel
#5, iChannel1
#10, iChannel2
#15, iChannel3
#16, iChannel4
#0, iNote
iOktave
iVolume
iEffekt
iInstrument
VAR
'Interface
word intreg[5 * 5]
'Sound Engine Stack
long hsnd_stack[18]
long cog1, cog2
'WavSynth Parameters
long snd_regs[48] 'Regs for Sound Hardware (8x6)+5dpcm
long dpcm_regs[5]
'DPCM Command Variables
word dpcmreg_ptr
'Global Hmus Player Vars
word tempo
word song_pc
word song_div
word song_ptr
word chfre[4]
byte chfx[4]
byte chvol[4]
byte hmus_state
byte hmvol
byte fxphs
'Sound FX Variables
word runlen[2]
word envamp[2]
word sfx_ptr[2]
byte envphs[2]
byte fmcnt[2], fmfreq[2]
byte loadsfx[2]
CON
'' Hss Master Control
PUB start : okay
stop
okay := cog1 := cognew(@entry, @snd_regs) + 1
okay := cog2 := cognew(hsound, @hsnd_stack) + 1
PUB stop
if cog1
cogstop(cog1~ - 1)
if cog2
cogstop(cog2~ - 1)
PUB peek(addrptr) : var1
var1 := LONG[@snd_regs][addrptr]
PUB intread(index): wert 'interface: auslesen eines interfaceregisters
wert := intreg[index]
CON
'' Hydra Music Commands
PUB hmus_load(songptr) | z
hmvol := 15
song_div := 0
song_ptr := songptr
song_pc := WORD[songptr][8]
tempo := WORD[songptr][12]
repeat z from 0 to 3
chfx[z] := 0
repeat z from 0 to 5*5 'interface: playerinterface alle werte löschen
intreg[z] := 0
PUB hmus_play
hmus_state := 1
PUB hmus_stop | z
hmus_state := 0
repeat z from 0 to 3
chvol[z] := 0
PUB hmus_pause
hmus_state := 0
PUB hmus_tempo(var1)
tempo := var1
PUB get_hmus_tempo : var1
var1 := tempo
PUB hmus_vol(var1)
hmvol := var1 <# 15 #> 0
PUB get_hmus_vol : var1
var1 := hmvol
CON
'' FXsynth Commands
PUB sfx_play(chan, soundptr)
if(chan == 1)
sfx_ptr[0] := soundptr
loadsfx[0] := 0
if(chan == 2)
sfx_ptr[1] := soundptr
loadsfx[1] := 0
PUB sfx_stop(chan)
if(chan == 1)
sfx_ptr[0] := 0
if(chan == 2)
sfx_ptr[1] := 0
PUB sfx_keyoff(chan)
if(chan == 1)
envphs[0] := 3
if(chan == 2)
envphs[1] := 3
CON
'' Hydra DPCM Commands
PUB dpcm_play(soundptr)
dpcmreg_ptr := soundptr
PUB dpcm_stop
dpcmreg_ptr := 1
CON
''*****************************
''* Hss Sound Engine *
''*****************************
PRI Hsound
repeat
'Update Music Engine
UpdateMus(song_ptr, Hmus_state) 'Update Music Player
'volume/frequenzwerte werden in die soundregister geschrieben
VolumeInterpol 'Delay and Interpolate Volume to Remove Pops and Clicks.
'Update DPCM Engine
if(dpcmreg_ptr)
DpcmUpdate 'Update the DPCM registers
'Update SoundFX Engine
'FX channel A
FXSynth(0,32)
'FX channel B
FXSynth(1, 40)
PRI VolumeInterpol | z, channelmul, musvar, freqval
fxphs += 5
'Volume Interpolation
repeat z from 0 to 3 step 1
channelmul := 4+(8*z)
musvar := (chvol[z]*(hmvol+1))&$F0
snd_regs[channelmul] := (snd_regs[channelmul] & 15)+musvar
'Freq Interpolation
channelmul -= 1 'Jump down a REG to Freq
musvar := chfre[z]<<16
if(chfx[z] == 0) 'None
snd_regs[channelmul] := musvar
elseif(chfx[z] < 3) 'Vibrato (light/hard)
if(fxphs < 128)
snd_regs[channelmul] := musvar+(chfre[z]<<(7+chfx[z]))
else
snd_regs[channelmul] := musvar-(chfre[z]<<(7+chfx[z]))
elseif(chfx[z] == 3) 'Tremolo
if(fxphs < 128)
snd_regs[channelmul] := musvar
else
snd_regs[channelmul] := musvar<<1
else 'Portamento
freqval := snd_regs[channelmul]>>16
if(freqval & $F000 == chfre[z] & $F000)
snd_regs[channelmul] := musvar
elseif(freqval < chfre[z])
snd_regs[channelmul] := snd_regs[channelmul]+(chfx[z]<<22)
else
snd_regs[channelmul] := snd_regs[channelmul]-(chfx[z]<<22)
PRI UpdateMus(songptr, state) | i, channel, channelmul, scrdat, freq, freqoct, flag
if(state == 0)
return ''Song is not playing.
song_div++ 'zeitfaktor; wird erhöht bis...
if(song_div => tempo) 'Tempo Divider 'schwellwert erreicht, dann nächster beat
song_div := 0
flag := 0
intreg[iBeatC] := intreg[iBeatC] + 1 'interface: beatconter erhöhen
intreg[iRowFlag] := 0 'interface: Kennung das Zeile bearbeitet wird
repeat i from 5 to 5*5 'interface: channelwerte löschen
intreg[i] := 0
repeat 'Score Decoder and Processor
scrdat := BYTE[song_ptr][song_pc] 'song_pc ist zeiger auf wert in MusicDat
channel := scrdat & 3 'untere zwei bit enthalten die kanalnummer
channelmul := channel<<3 'jedem channel sind 8 registerwerte zugeordent
intreg[iEngineC] := song_pc 'interface: enginecounter setzen
song_pc++ 'zeiger auf nächsten wert setzen
''Base Commands
if(scrdat == 0) 'End Row 'nächste trackerzeile
intreg[iRowFlag] := 1 'interface: Zeile fertig bearbeitet
quit
if(scrdat == 1) 'Repeat Song 'wiederholt ab MusicLoop (MusicDat ist also die einleitung)
song_pc := WORD[songptr][9]
intreg[iRepeat] := intreg[iRepeat] + 1 'interface: flag das songende erreicht wurde
quit
if(scrdat == 2) 'End Song 'status wird auf 0 gesetzt
intreg[iEndFlag] := 1 'interface: flag das songende erreicht wurde
hmus_stop
quit
if(scrdat == 3) 'Set Flag
flag := 1
next
if((scrdat & $3C) == $20) 'Patch HI Note 'oktave erhöhen und veränderung zu "Change Note"
flag := 2
scrdat := scrdat>>3
scrdat += 64+channel
if(scrdat & 4) 'Change Note
freq := scrdat>>3 'note Bit3 bis Bit7 (32 Noten)
freqoct := freq/12
freq -= freqoct*12
case flag
1 : freqoct += 2
2 : freqoct += 6
other : freqoct += 4
flag := 0
snd_regs[4+channelmul] := snd_regs[4+channelmul] & $FE
intreg[(channel*iChannel)+iChannel+iNote] := freq + 1 'interface: note setzen (0 ist erste note!)
intreg[(channel*iChannel)+iChannel+iOktave] := freqoct 'interface: oktave setzen
'frequenz aus tabelle holen
'je nach oktave wird nach rechts verschoben (/2)
chfre[channel] := NoteFreqs[freq]>>(6-freqoct)
snd_regs[4+channelmul] := (snd_regs[4+channelmul] & $FE)+1
next 'Repeat To Next Datum
if(scrdat & 8) 'Change Evelope / Channel Effect
if(flag)
intreg[(channel*iChannel)+iChannel+iEffekt] := scrdat>>4 + 1 'interface: effektwert setzen
chfx[channel] := scrdat>>4
flag := 0
else
intreg[(channel*iChannel)+iChannel+iVolume] := scrdat>>4 'interface: volume setzen
chvol[channel] := scrdat>>4
next 'Repeat To Next Datum
if(scrdat & 16) 'Change Instrument
freq := (scrdat & $E0)>>3
freq += flag<<5
flag := 0
intreg[(channel*iChannel)+iChannel+iInstrument] := freq>>2 + 1 'interface: instrument setzen
snd_regs[0+channelmul] := songptr+WORD[songptr+32][freq] 'zeiger auf neues instrumentensample
snd_regs[1+channelmul] := WORD[songptr+32][freq+1] 'ende des samples
snd_regs[2+channelmul] := WORD[songptr+32][freq+2] 'loop
snd_regs[4+channelmul] := WORD[songptr+32][freq+3] & $0F 'flags?
next 'Repeat To Next Datum
if(scrdat & 64) 'Detune
chfre[channel] := chfre[channel]+(chfre[channel]>>8)
PRI DpcmUpdate
if(dpcmreg_ptr > 15) 'Play Sample.
dpcm_regs[2] := 65535 'End sample if one was playing
dpcm_regs[0] := dpcmreg_ptr+8
dpcm_regs[4] := 128
dpcm_regs[3] := LONG[dpcmreg_ptr][1] 'Get sampling rate
dpcm_regs[1] := WORD[dpcmreg_ptr][1] 'Get length
dpcm_regs[2] := 0 'Reset play counter
elseif(dpcmreg_ptr == 1) 'Stop Sample
dpcm_regs[2] := 65535 'End sample
dpcm_regs[4] := 128
dpcmreg_ptr := 0
PRI FXSynth(SoundVars, ChannelFX) | TimeCnt, SoundFX, Modwav, FMwav, AMwav
TimeCnt := Cnt
SoundFX := sfx_ptr[SoundVars]
if(loadsfx[SoundVars] == 0)
'Setup OSC WaveForm
case BYTE[SoundFX][0]
$00: 'Sine
snd_regs[ChannelFX] := @SineTable
snd_regs[1+ChannelFX] := 64
$01: 'Fast Sine
snd_regs[ChannelFX] := @FastSine
snd_regs[1+ChannelFX] := 32
$02: 'Sawtooth
snd_regs[ChannelFX] := @Sawtooth
snd_regs[1+ChannelFX] := 64
$03: 'Square
snd_regs[ChannelFX] := @SqrTable
snd_regs[1+ChannelFX] := 32
$04: 'Fast Square
snd_regs[ChannelFX] := @FastSqr
snd_regs[1+ChannelFX] := 8
$05: 'Buzz
snd_regs[ChannelFX] := @NoteFreqs
snd_regs[1+ChannelFX] := 24
$06: 'Noise
snd_regs[ChannelFX] := $F002
snd_regs[1+ChannelFX] := 3000
snd_regs[2+ChannelFX] := 0
snd_regs[4+ChannelFX] := $01
loadsfx[SoundVars] := 1
runlen[SoundVars] := 0
fmcnt[SoundVars] := 0
fmfreq[SoundVars] := 0
envamp[SoundVars] := 0
envphs[SoundVars] := 0
''Modulation Code
fmfreq[SoundVars]++
if(fmfreq[SoundVars] => BYTE[SoundFX][4])
fmfreq[SoundVars] := 0
fmcnt[SoundVars]++
fmcnt[SoundVars] := fmcnt[SoundVars] & $3F
case BYTE[SoundFX][5]
$00:
Modwav := BYTE[@SineTable][fmcnt[SoundVars]]
$01:
Modwav := BYTE[@FastSine][fmcnt[SoundVars] & 31]
$02:
Modwav := fmcnt[SoundVars]<<2
$03:
Modwav := !fmcnt[SoundVars]<<2
$04:
if(fmcnt[SoundVars] & 8)
Modwav := $ff
else
Modwav := $00
$05:
Modwav := BYTE[$F002][fmcnt[SoundVars]]
$FF:
Modwav := BYTE[SoundFX+12][fmcnt[SoundVars] & 15]
fmwav := Modwav/(BYTE[SoundFX][6]) 'FM amount
amwav := 256-(Modwav/(BYTE[SoundFX][7])) 'AM amount
amwav := (BYTE[SoundFX][3]*amwav)>>8
''Envelope Generator
if(envphs[SoundVars] == 0) 'Attack
envamp[SoundVars] += BYTE[SoundFX][8]
if(envamp[SoundVars] > 8191)
envamp[SoundVars] := 8191
envphs[SoundVars] := 1
if(BYTE[SoundFX][8] == $ff)
envamp[SoundVars] := 8191
if(envphs[SoundVars] == 1) 'Decay
envamp[SoundVars] -= BYTE[SoundFX][9]
if(envamp[SoundVars] & $8000)
envphs[SoundVars] := 2
if(envamp[SoundVars] =< (BYTE[SoundFX][10]<<5))
envphs[SoundVars] := 2
if(envphs[SoundVars] == 2) 'Sustain
envamp[SoundVars] := (BYTE[SoundFX][10]<<5)
if(envphs[SoundVars] == 3) 'Release
envamp[SoundVars] -= BYTE[SoundFX][11]
if(envamp[SoundVars] & $8000)
envamp[SoundVars] := 4
amwav := ((envamp[SoundVars]>>9)*(amwav+1))>>4
''Run Length and Outputing
if(SoundFX > 15)
runlen[SoundVars]++
snd_regs[3+ChannelFX] := (BYTE[SoundFX][2]+fmwav)<<24 'Update Frequency
snd_regs[4+ChannelFX] := (amwav<<4)+(snd_regs[4+ChannelFX] & $0F) 'Update Amplitude
else
snd_regs[4+ChannelFX] := $00 'Mute
if(BYTE[SoundFX][1] == $ff) '$ff = never stop
runlen[SoundVars] := 0
if(runlen[SoundVars] > (BYTE[SoundFX][1]<<5)) 'Duration KeyOff
envphs[SoundVars] := 3
WaitCnt(TimeCnt + 52_000) ''Delay for Synth Engine Update.
DAT
SineTable byte $80, $8c, $98, $a5, $b0, $bc, $c6, $d0
byte $da, $e2, $ea, $f0, $f5, $fa, $fd, $fe
byte $ff, $fe, $fd, $fa, $f5, $f0, $ea, $e2
byte $da, $d0, $c6, $bc, $b0, $a5, $98, $8c
byte $80, $73, $67, $5a, $4f, $43, $39, $2f
byte $25, $1d, $15, $0f, $0a, $05, $02, $01
byte $00, $01, $02, $05, $0a, $0f, $15, $1d
byte $25, $2f, $39, $43, $4f, $5a, $67, $73
Sawtooth byte $ff, $fb, $f7, $f3, $ef, $eb, $e7, $e3
byte $df, $db, $d7, $d3, $cf, $cb, $c7, $c3
byte $bf, $bb, $b7, $b3, $af, $ab, $a7, $a3
byte $9f, $9b, $97, $93, $8f, $8b, $87, $83
byte $80, $7c, $78, $74, $70, $6c, $68, $64
byte $60, $5c, $58, $54, $50, $4c, $48, $44
byte $40, $3c, $38, $34, $30, $2c, $28, $24
byte $20, $1c, $18, $14, $10, $0c, $08, $04
FastSine byte $80, $98, $b0, $c6, $da, $ea, $f5, $fd
byte $ff, $fd, $f5, $ea, $da, $c6, $b0, $98
byte $80, $67, $4f, $39, $25, $15, $0a, $02
byte $00, $02, $0a, $15, $25, $39, $4f, $67
SqrTable byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff
byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff
byte $00, $00, $00, $00, $00, $00, $00, $00
byte $00, $00, $00, $00, $00, $00, $00, $00
FastSqr byte $ff, $ff, $ff, $ff, $00, $00, $00, $00
'Note LookupTable.
NoteFreqs word $85F3, $8DEA, $965B, $9F4B, $A8C4, $B2CD, $BD6F, $C8B3, $D4A2, $E147, $EEAC, $FCDE 'Top Octave Lookup
CON
''*****************************
''* WaveTable Synth v1.2 *
''* DPCM Synth v1.1 *
''* (C)2006 Andrew Arsenault *
''*****************************
DAT
org
entry mov dira,Port_Pins 'Setup output pins
mov ctra,Right_ctra 'Setup Right Audio Channel
mov ctrb,Left_ctra 'Setup Left Audio Channel
mov ChlA_wave,#256 'Set channel signals.
mov ChlA_offset,#0 'Set channel's offset.
mov ChlA_counter,#0
mov Time,#10
add Time,cnt 'Prepare for asm type WAITCNT loop.
'MAIN LOOP
update waitcnt Time,Timing_delay 'Wait for CNT = D, then add S into D
'Transfer Sound Registers
mov addrregs,par
mov y,NumberOfChannels
'Fetch Channel's Registers
transferchl rdlong ChlAp_sampptr,addrregs
add addrregs,#4
rdlong ChlAp_sampend,addrregs
add addrregs,#4
rdlong Ch1Ap_samplpp,addrregs
add addrregs,#4
rdlong Ch1Ap_freq,addrregs
add addrregs,#4
rdlong ChlAp_keyon,addrregs
'Fetch Channel's Static Variables
add addrregs,#8
rdlong ChlA_offset,addrregs
add addrregs,#4
rdlong ChlA_counter,addrregs
'Run Synth Engine on Channel
call #wvsynth
'Store Channel's Static Variables (Tucked Center X move to Wave)
wrlong ChlA_counter,addrregs
sub addrregs,#4
sub x,#256
wrlong ChlA_offset,addrregs
sub addrregs,#4
mov ChlA_wave,x 'Doesn't Waste anything doing this.
wrlong ChlA_wave,addrregs
add addrregs,#12
'Loop Until All Channel's Are Done.
djnz y,#transferchl
'Run DPCM Engine
call #dpcm
'Mix Channels Together
mov addrregs,par
mov ChlA_wave,#0
add addrregs,#5*4
mov y,NumberOfChannels
mixchls rdlong x,addrregs
add ChlA_wave,x
add addrregs,#8*4
djnz y,#mixchls
mov x,DPCM_wave 'Add DPCM
shl x,#2
add ChlA_wave,x
shl ChlA_wave,#20 'Convert 12bit singal into a 32bit one.
'Update output Channels then repeat again.
mov frqa,ChlA_wave
mov frqb,ChlA_wave
jmp #update
'-------------------------Dpcm Engine-------------------------'
dpcm mov addrregs,par
add addrregs,#192
rdlong DPCM_address,addrregs 'Start Address
add addrregs,#4
rdlong DPCM_runlen,addrregs 'File Lenght
add addrregs,#4
rdlong DPCM_offset,addrregs 'File Offset
add addrregs,#4
rdlong DPCM_freq,addrregs 'Playback Speed
add addrregs,#4
rdlong DPCM_wave,addrregs 'Waveform Amp
'Check for if keyon/length is set.
cmp DPCM_offset,DPCM_runlen wc
if_ae jmp #mute_dpcm 'End of file
'Freq Timer/Divider and Increase sampling offset
add DPCM_counter,DPCM_freq wc
if_nc jmp #done_dpcm
'Decode DPCM
add DPCM_address,DPCM_offset
rdbyte x,DPCM_address 'Fetch Datum
mov DPCM_delta,x
shr DPCM_delta,#6
mov y,#1
shl y,DPCM_delta
mov DPCM_delta,y
mov y,#1
shl y,DPCM_phs
test x,y wc
if_c add DPCM_wave,DPCM_delta
if_nc sub DPCM_wave,DPCM_delta
add DPCM_phs,#1
cmp DPCM_phs,#6 wc
if_b jmp #done_dpcm
mov DPCM_phs,#0
add DPCM_offset,#1
jmp #done_dpcm
mute_dpcm mov DPCM_wave, #128
done_dpcm mov addrregs,par
add addrregs,#200
wrlong DPCM_offset,addrregs 'File Offset
add addrregs,#8
wrlong DPCM_wave,addrregs 'Wave
dpcm_ret ret
'-----------------------Dpcm Engine End-----------------------'
'-------------------------Sound Engine-------------------------'
'Freq Timer/Divider and Increase sampling offset
wvsynth add ChlA_counter,Ch1Ap_freq wc
if_c add ChlA_offset,#1
'Reset sample position and lock at zero if Keyoff.
test ChlAp_keyon,#%0001 wc
if_nc mov ChlA_offset,#0
'Reset(loop) if needed
cmp ChlA_offset,ChlAp_sampend wc
if_ae mov ChlA_offset,Ch1Ap_samplpp
'Check BitRate and Set Offset
mov x,ChlA_offset
test ChlAp_keyon,#%0010 wc
if_c shr x,#1
'Fetch WaveTable
mov ChlA_wave,ChlAp_sampptr
add ChlA_wave,x
rdbyte ChlA_wave,ChlA_wave
'Check BitRate and Skip if 8bit
test ChlAp_keyon,#%0010 wc
if_nc jmp #skip_4bitsam
'Convert 4bit to 8bit
test ChlA_offset,#%0001 wc
if_c shr ChlA_wave,#4
if_nc and ChlA_wave,#%00001111
mov x,ChlA_wave
shl ChlA_wave,#4
add ChlA_wave,x
'Center Amplitude and mute if Keyoff.
skip_4bitsam test ChlAp_keyon,#%0001 wc
if_nc mov ChlA_wave,#128
'Volume Multiply
mov x,#0
test ChlAp_keyon,#%10000000 wc
if_c add x,ChlA_wave
if_nc add x,#128
shr ChlA_wave,#1
test ChlAp_keyon,#%01000000 wc
if_c add x,ChlA_wave
if_nc add x,#64
add x,#64
shr ChlA_wave,#1
test ChlAp_keyon,#%00100000 wc
if_c add x,ChlA_wave
if_nc add x,#32
add x,#96
shr ChlA_wave,#1
test ChlAp_keyon,#%00010000 wc
if_c add x,ChlA_wave
if_nc add x,#16
add x,#112
'Return Audio as X.
wvsynth_ret ret
'-----------------------Sound Engine End-----------------------'
Port_Pins long %00000000_00000000_00000011_00000000
'- CTR PLL -------- BPIN --- APIN
Right_ctra long %0_00110_000_00000000_000000_000_001000
Left_ctra long %0_00110_000_00000000_000000_000_001001
Timing_delay long 2500 'Sampling Rate = 32,000.00hz
NumberOfChannels long 6
Time res 1
addrregs res 1
x res 1
y res 1
'WaveTable Synth Accumulators
ChlA_wave res 1
ChlA_offset res 1
ChlA_counter res 1
ChlAp_sampptr res 1
ChlAp_sampend res 1
Ch1Ap_samplpp res 1
Ch1Ap_freq res 1
ChlAp_keyon res 1
'DPCM Accumulators
DPCM_wave res 1
DPCM_address res 1
DPCM_offset res 1
DPCM_counter res 1
DPCM_freq res 1
DPCM_runlen res 1
DPCM_phs res 1
DPCM_delta res 1

Binary file not shown.

View File

@ -0,0 +1,165 @@
' ASM WAV Player Ver. 1b (Plays only stereo, 16-bit PCM WAV files from SD card)
' Copyright 2007 Raymond Allen See end of file for terms of use.
' Settings for Demo Board Audio Output: Right Pin# = 10, Left Pin# = 11 , VGA base=Pin16, TV base=Pin12
' Rev.B: 21Dec07 Fixed pin assignment bug.
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000 '80 MHz
buffSize = 100
VAR long parameter1 'to pass @buff1 to ASM
long parameter2 'to pass @buff2 to ASM
long parameter3 'to pass sample rate to ASM
long parameter4 'to pass #samples to ASM
long buff1[buffSize]
long buff2[buffSize]
long cog
byte Header[44]
PUB start(para_ptr) 'startet eine cog als wav-player
'' para_ptr - zeiger auf vier long-parameter
stop
'Start ASM player in a new cog
cog := COGNEW(@ASMWAV,para_ptr) + 1
PUB stop
if cog
cogstop(cog~ - 1)
DAT
ORG 0
ASMWAV
'load input parameters from hub to cog given address in par
mov y, par
rdlong pData1, y
add y, #4
rdlong pData2, y
add y, #4
rdlong dRate, y
add y, #4
rdlong nSamples, y
setup
'setup output pins
MOV DMaskR,#1
ROL DMaskR,OPinR
OR DIRA, DMaskR
MOV DMaskL,#1
ROL DMaskL,OPinL
OR DIRA, DMaskL
'setup counters
OR CountModeR,OPinR
MOV CTRA,CountModeR
OR CountModeL,OPinL
MOV CTRB,CountModeL
'Wait for SPIN to fill table
MOV WaitCount, CNT
ADD WaitCount,BigWait
WAITCNT WaitCount,#0
'setup loop table
MOV LoopCount,SizeBuff
'ROR LoopCount,#1 'for stereo
MOV pData,pData1
MOV nTable,#1
'setup loop counter
MOV WaitCount, CNT
ADD WaitCount,dRate
MainLoop
SUB nSamples,#1
CMP nSamples,#0 wz
IF_Z JMP #Done
waitcnt WaitCount,dRate
RDLONG Right,pData
ADD Right,twos 'Going to cheat a bit with the LSBs here... Probably shoud fix this!
MOV FRQA,Right
ROL Right,#16 '16 LSBs are left channel...
MOV FRQB,Right
WRLONG Zero,pData
ADD pData,#4
'loop
DJNZ LoopCount,#MainLoop
MOV LoopCount,SizeBuff
'switch table ?
CMP nTable,#1 wz
IF_Z JMP #SwitchToTable2
SwitchToTable1
MOV nTable,#1
MOV pData,pData1
JMP #MainLoop
SwitchToTable2
MOV nTable,#2
MOV pData,pData2
JMP #MainLoop
Done
'now stop
COGID thisCog
COGSTOP thisCog
'Working variables
thisCog long 0
x long 0
y long 0
dlsb long 1 << 9
BigWait long 100000
twos long $8000_8000
'Loop parameters
nTable long 0
WaitCount long 0
pData long 0
LoopCount long 0
SizeBuff long buffsize
'Left long 0
Right long 0
Zero long 0
'setup parameters
DMaskR long 0 'right output mask
OPinR long 8 'right channel output pin # ' <--------- Change Right pin# here !!!!!!!!!!!!!!
DMaskL long 0 'left output mask
OPinL long 9 'left channel output pin # ' <--------- Change Left pin# here !!!!!!!!!!!!!!
CountModeR long %00011000_00000000_00000000_00000000
CountModeL long %00011000_00000000_00000000_00000000
'input parameters
pData1 long 0 'Address of first data table
pData2 long 0 'Address of second data table
dRate long 5000 'clocks between samples
nSamples long 2000
{{
TERMS OF USE: MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
}}

Binary file not shown.