This commit is contained in:
parent
0e2ad4b175
commit
1009b1cf3f
|
@ -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.
|
||||
}}
|
|
@ -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.
|
@ -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.
Loading…
Reference in New Issue