diff --git a/flash/administra/admflash-fsrw.spin b/flash/administra/admflash-fsrw.spin new file mode 100644 index 0000000..4a27665 --- /dev/null +++ b/flash/administra/admflash-fsrw.spin @@ -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. +}} \ No newline at end of file diff --git a/flash/administra/admflash-hss.spin b/flash/administra/admflash-hss.spin new file mode 100644 index 0000000..adbcd82 --- /dev/null +++ b/flash/administra/admflash-hss.spin @@ -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 \ No newline at end of file diff --git a/flash/administra/admflash-sdspiqasm.spin b/flash/administra/admflash-sdspiqasm.spin new file mode 100644 index 0000000..4ffb9d1 Binary files /dev/null and b/flash/administra/admflash-sdspiqasm.spin differ diff --git a/flash/administra/admflash-wav.spin b/flash/administra/admflash-wav.spin new file mode 100644 index 0000000..2d8c8f8 --- /dev/null +++ b/flash/administra/admflash-wav.spin @@ -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. +}} \ No newline at end of file diff --git a/flash/administra/admflash.spin b/flash/administra/admflash.spin new file mode 100644 index 0000000..61cbb27 Binary files /dev/null and b/flash/administra/admflash.spin differ