Browse Source

Initial commit!

tags/1.0
PHENOM 2 years ago
commit
4f22c11b16

BIN
DemoBoard/Board layout with parts.png View File


BIN
DemoBoard/Board layout.png View File


BIN
DemoBoard/Schematics/Sheet1 main.png View File


BIN
DemoBoard/Schematics/Sheet10 button LED interface.png View File


BIN
DemoBoard/Schematics/Sheet2 programmer.png View File


BIN
DemoBoard/Schematics/Sheet3 power supply.png View File


BIN
DemoBoard/Schematics/Sheet4 audio circuit.png View File


BIN
DemoBoard/Schematics/Sheet5 VGA circuit.png View File


BIN
DemoBoard/Schematics/Sheet6 keyboard mouse interface.png View File


BIN
DemoBoard/Schematics/Sheet7 EEPROM.png View File


BIN
DemoBoard/Schematics/Sheet8 TV RCA interface.png View File


BIN
DemoBoard/Schematics/Sheet9 SD Card interface.png View File


+ 45
- 0
README.md View File

@@ -0,0 +1,45 @@
# Propeller Parallax P8X32A SIDcog+VGA+Mouse+Keyboard+MicroSD DemoBoard

A Project I've worked on during University which demonstrates the multi-cog approach of the Parallax Propeller Multicore Microcontroller in combination with my selfbuilt DemoBoard.

## Features

- Commodore C64 SID (Sound Interface Device) MOS8580 Emulation via SIDcog outputted to the 3.5mm audio jack
- Reading dumped *.sid data from a FAT16/32 formatted MicroSD Card via SPI
- TextMode based Terminal VGA Output
- PS/2 Interfacing with mouse and keyboard

## Schematics

The Schematics can be found in the ./DemoBoard/Schematics/ folder

## Demoboard

### Board Layout with Parts

![Board Layout with Parts](DemoBoard/Board layout with parts.png)

### Board Layout ready for print (Single Sided)

Use wire for the top-layer connections. It's not that much wiring anyways.

![](DemoBoard/Board layout.png)

EAGLE PCB Layout and Schematics will be provided soon.

## How to use

Format an MicroSD Card with either FAT16 or FAT32 as filesystem and use the bundled SID_Dumper.exe to dump the register data from \*.sid files. Put the resulted dump-file (\*.dmp) on your MicroSD Card.

## Compiling

Use the [Propeller Tool Software](https://www.parallax.com/downloads/propeller-tool-software-windows-spin-assembly) and compile src/Main.spin as top module and load it onto the EEPROM or RAM.

## Contributors

- Tomas Rokicki and Jonathan Dummer - FAT Filesystem Driver
- Jonathan "lonesock" Dummer - SPI interface routines for SD & SDHC & MMC cards
- Chip Gracey, Jon Williams - Simple Numbers String conversion, VGA High-Res Text Driver, PS/2 Mouse Driver, PS/2 Keyboard Driver
- Allen Marincak - VGA High-Res Text UI Elements Base UI Support
- Johannes Ahlebrand - SIDcog - SID/MOS8580 emulator
- PHENOM - DemoBoard, Bundling everything together in Main.spin, GUI

BIN
SIDDumper/CombinedWaveforms.bin View File


BIN
SIDDumper/QtCore4.dll View File


BIN
SIDDumper/QtGui4.dll View File


BIN
SIDDumper/SID_Dumper.exe View File


BIN
SIDDumper/libgcc_s_dw2-1.dll View File


BIN
SIDDumper/libportaudio-2.dll View File


BIN
SIDDumper/mingwm10.dll View File


BIN
src/CombinedWaveforms.bin View File


BIN
src/GUIBase.spin View File


BIN
src/InputField.spin View File


BIN
src/Keyboard.spin View File


+ 224
- 0
src/Main.spin View File

@@ -0,0 +1,224 @@
{
Propeller Parallax SIDcog+VGA+MOUSE+KEYBOARD Demo
This software emulates the Commodore 64 SID Chip from the MicroSD Card slot.
Additionally there is VGA Output and PS/2 Mouse/Keyboard input.
Written by PHENOM
}

con
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

startPlayRate = 75'Hz
rightPin = 10
leftPin = 11

sd_DO = 8
sd_CLK = 7
sd_DI = 6
sd_CS = 5

vga_base = 16 'VGA - VSync
mouse_dat = 24 'MOUSE data
mouse_clk = 25 'MOUSE clock
keyboard_dat = 26 'KEYBOARD data
keyboard_clk = 27 'KEYBOARD clock

var
byte playRate
byte buffer[25]
byte vga_rows, vga_cols
byte playMusicButton
byte pauseMusicButton
byte stopMusicButton
byte incSpeedButton
byte decSpeedButton
byte titlefileNames
byte radioButtonCount
byte inputBuf[80]
byte radiobuttons[30]
byte okButton
byte commandInput
byte commandLog
byte musicPlaying
byte musicPaused
long stack[20]
obj
GUI: "GUIBase"
SID: "SIDcog"
SD: "fsrw"
NUMS: "simple_numbers"
PUB Main | idx, gx
playRate:=startPlayRate
sd.mount_explicit(sd_DO, sd_CLK, sd_DI, sd_CS)

CreateUI
listRoot
musicPlaying:=0
musicPaused:=0
GUI.RADBSelect(radiobuttons[0], 1)
GUI.INPFSelect(commandInput,1)
cognew(ledBlinking, @stack)

'Main loop
repeat
gx := GUI.ProcessUI
playMusic
case gx
playMusicButton:
playFile
pauseMusicButton:
pauseMusic
stopMusicButton:
stopMusic
decSpeedButton:
decSpeed
incSpeedButton:
incSpeed
okButton:
executeCommand
commandInput:
executeCommand
idx := GUI.GetMouseXY
GUI.PrintStr( vga_rows-1, 17, NUMS.decf( idx >> 8, 2 ), 0 )
GUI.PrintStr( vga_rows-1, 22, NUMS.decf( idx & $FF, 2 ), 0 )

pri executeCommand
GUI.INPFGetString(commandInput, @inputBuf)
if(strcomp(@inputBuf,string("stop")))
stopMusic
return
if(strcomp(@inputBuf,string("play")))
playFile
return
if(strcomp(@inputBuf,string("pause")))
pauseMusic
return

addToLog(@inputBuf)
return
pri listRoot | bufPos , id
bufPos:=0
radioButtonCount:=0
sd.opendir

repeat while 0 == sd.nextfile(@buffer)
radiobuttons[radioButtonCount]:=GUI.RADBInit(5+radioButtonCount,20,15, @buffer, 15)
fileNamesP[radioButtonCount]:=@fileNames + bufPos
bytemove(@fileNames + bufPos, @buffer,strsize(@buffer)+1)
bufPos+=strsize(@buffer)+1
radioButtonCount++

pri ledBlinking | i
dira&=NOT((1<<0) | (1<<1) | (1<<2))
dira|=24
outa|=24
repeat
if ((ina & (1<<0)) == 0)
addToLog(string("Key 1"))
if ((ina & (1<<1)) == 0)
addToLog(string("Key 2"))
if ((ina & (1<<2)) == 0)
addToLog(string("Key 3"))

waitcnt(cnt + (clkfreq/2))
outa^=24

pri playFile | i
if(musicPaused==1)
sid.setvolume(15)
musicPaused:=0
return

if(musicPlaying==0)
addToLog(string("Start music"))
musicPlaying:=1
sid.start(rightPin,leftPin)
repeat while i<radioButtonCount
if(GUI.RADBIsSet(radiobuttons[i])==true)
sd.popen(fileNamesP[i], "r")
i++

pri playMusic
if(musicPlaying==1 and musicPaused==0)
waitcnt(cnt + (clkfreq/playRate))
sd.pread(@buffer,25)
sid.updateRegisters(@buffer)

pri stopMusic
if(musicPlaying==1)
addToLog(string("Stop music"))
musicPlaying:=0
sid.stop

pri pauseMusic
addToLog(string("Pause music"))
musicPaused:=1
sid.setvolume(0)
pri incSpeed
addToLog(string("Increase speed"))
playRate+=5
pri decSpeed
addToLog(string("Decrease speed"))
playRate-=5

pri addToLog(textPtr)
GUI.TBOXPrint( commandLog, textPtr, 0 )
pri CreateUI | tmp
tmp := GUI.Init(vga_base, mouse_dat, mouse_clk, keyboard_dat, keyboard_clk )
vga_rows := ( tmp & $0000FF00 ) >> 8
vga_cols := tmp & $000000FF

GUI.ClearScreen( %%010, %%222 )
commandLog:=GUI.TBOXInit( vga_rows-14, 0, vga_cols, 10, 0, @_sLog )
GUI.SBOXInit( 2, 0, 19, 19, @_sControl ) 'Musik Box
GUI.SBOXInit( 2, 19, 19, 19, @_sChooseTitle ) 'Datei Auswahl
GUI.SetLineColor(0,%%222,%%111)'Titel
GUI.PrintStr(0,(vga_cols/2)-(strsize(@_sTitle)/2),@_sTitle, 0 )
GUI.SetLineColor(1,%%222,%%111)'Name
GUI.PrintStr(1,(vga_cols/2)-(strsize(@_sMyName)/2),@_sMyName, 0 )
GUI.SetLineColor(vga_rows-1,%%222,%%111) 'Status Zeile
GUI.PrintStr(vga_rows-1,0,@_sMousePosition, 0 )


commandInput:=GUI.INPFInit(vga_rows-4, 0, vga_cols-strsize(@_sExecute)-2, 1, @_sCommand )
okButton:=GUI.PUSHInit(vga_rows-4, vga_cols-strsize(@_sExecute)-2, @_sExecute)
playMusicButton:=GUI.PUSHInit(5, 1, @_sPlayMusic)
stopMusicButton:=GUI.PUSHInit(8, 1, @_sStopMusic)
pauseMusicButton:=GUI.PUSHInit(11, 1, @_pauseMusic)
incSpeedButton:=GUI.PUSHInit(14, 1, @_sSpeedInc)
decSpeedButton:=GUI.PUSHInit(17, 1, @_sSpeedDec)

dat
_sChooseTitle byte "Song chooser",0
_sTitle byte "SIDcog Demo",0
_sMyName byte "PHENOM",0
_sControl byte "Control",0
_sMousePosition byte "Mouse position: X=xx Y=xx",0
_sPlayMusic byte "Play music",0
_pauseMusic byte "Pause",0
_sStopMusic byte "Stop music",0
_sSpeedInc byte "+Speed..",0
_sSpeedDec byte "-Speed..",0
_sCommand byte "Command:",0
_sExecute byte "Run",0
_sLog byte "Log",0

fileNames byte 0[512]
fileNamesP long 0[20] 'Contains the adresses of files

BIN
src/MenuItem.spin View File


BIN
src/PushButton.spin View File


BIN
src/RadioCheck.spin View File


BIN
src/SIDcog.spin View File


BIN
src/SimpleBox.spin View File


BIN
src/SpinBox.spin View File


BIN
src/StatusLamp.spin View File


BIN
src/TextBox.spin View File


+ 605
- 0
src/fsrw.spin View File

@@ -0,0 +1,605 @@
{{
' fsrw 2.2 Copyright 2009 Tomas Rokicki and Jonathan Dummer
'
' See end of file for terms of use.
'
' This object provides FAT16/32 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.
' v2.1 12 July 2009 FAT32, SDHC, multiblock, bug fixes
}}
'
' Constants describing FAT volumes.
'
con
SECTORSIZE = 512
SECTORSHIFT = 9
DIRSIZE = 32
DIRSHIFT = 5
'
' The object that provides the block-level access.
'
obj
sdspi: "mb_spi"
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
'
' Variables used when mounting to describe the FAT layout of the card.
'
long filesystem ' 0 = unmounted, 1 = fat16, 2 = fat32
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
long endofchain ' end of chain marker (with a 0 at the end)
'
' 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
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)
if (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 brclust(b)
'
' Read a cluster entry.
'
if (filesystem == 1)
return brword(b)
else
return brlong(b)
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)
pri brwclust(w, v)
'
' Write a cluster entry.
if (filesystem == 1)
brwword(w, v)
else
brwlong(w, v)

pri getfstype : r
if (brlong(@buf+$36) == constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)) and buf[$3a]=="6")
return 1
if (brlong(@buf+$52) == constant("F" + ("A" << 8) + ("T" << 16) + ("3" << 24)) and buf[$56]=="2")
return 2
' return r (default return)
pub mount_explicit(DO, CLK, DI, CS) : r | 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_explicit(DO, CLK, DI, CS)
lastread := -1
dirty := 0
sdspi.readblock(0, @buf)
if (getfstype > 0)
start := 0
else
start := brlong(@buf+$1c6)
sdspi.readblock(start, @buf)
filesystem := getfstype
if (filesystem == 0)
abort(-20) ' not a fat16 or fat32 volume
if (brword(@buf+$0b) <> SECTORSIZE)
abort(-21) ' bad bytes per sector
sectorspercluster := buf[$0d]
if (sectorspercluster & (sectorspercluster - 1))
abort(-22) ' bad sectors per cluster
clustershift := 0
repeat while (sectorspercluster > 1)
clustershift++
sectorspercluster >>= 1
sectorspercluster := 1 << clustershift
reserved := brword(@buf+$0e)
if (buf[$10] <> 2)
abort(-23) ' not two FATs
sectors := brword(@buf+$13)
if (sectors == 0)
sectors := brlong(@buf+$20)
fat1 := start + reserved
if (filesystem == 2)
rootentries := 16 << clustershift
sectorsperfat := brlong(@buf+$24)
dataregion := (fat1 + 2 * sectorsperfat) - 2 * sectorspercluster
rootdir := (dataregion + (brword(@buf+$2c) << clustershift)) << SECTORSHIFT
rootdirend := rootdir + (rootentries << DIRSHIFT)
endofchain := $ffffff0
else
rootentries := brword(@buf+$11)
sectorsperfat := brword(@buf+$16)
rootdir := (fat1 + 2 * sectorsperfat) << SECTORSHIFT
rootdirend := rootdir + (rootentries << DIRSHIFT)
dataregion := 1 + ((rootdirend - 1) >> SECTORSHIFT) - 2 * sectorspercluster
endofchain := $fff0
if (brword(@buf+$1fe) <> $aa55)
abort(-24) ' bad FAT signature
totclusters := ((sectors - dataregion + start) >> clustershift)
' return r (default return)
'
' For compatibility, a single pin.
'
pub mount(basepin) : r | start, sectorspercluster, reserved, rootentries, sectors
return mount_explicit(basepin, basepin+1, basepin+2, basepin+3)
'
' This may do more complicated stuff later.
'
pub unmount
pclose
sdspi.readblock(0, @buf)
sdspi.stop
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 << filesystem)
return readbytec(fatptr)
pri followchain : r
'
' Follow the fat chain and update the writelink.
'
r := brclust(readfat(fclust))
writelink := fatptr
' return r (default return)
pri nextcluster : r
'
' 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.
'
r := followchain
if (r < 2 or r => totclusters)
abort(-9) ' bad cluster value
' return r (default return)
pri freeclusters(clust) | bp
'
' Free an entire cluster chain. Used by remove and by overwrite.
' Assumes the pointer has already been cleared/set to end of chain.
'
repeat while (clust < endofchain)
if (clust < 2)
abort(-26) ' bad cluster number")
bp := readfat(clust)
clust := brclust(bp)
brwclust(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(rcnt, metadata) : r | 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 (rcnt > 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 & (!((SECTORSIZE >> filesystem) - 1))
count := 2
repeat
readfat(cluststart)
repeat i from 0 to SECTORSIZE - 1<<filesystem step 1<<filesystem
if (buf2[i] == 0)
if (brclust(@buf2+i) == 0)
newcluster := cluststart + (i >> filesystem)
if (newcluster => totclusters)
newcluster := -1
quit
if (newcluster > 1)
brwclust(@buf2+i, endofchain+$f)
if (writelink == 0)
brwword(readbytec(direntry)+$1a, newcluster)
writelink := (direntry&(SECTORSIZE-filesystem))
brwlong(@buf2+writelink+$1c, floc+bufat)
if (filesystem == 2)
brwword(@buf2+writelink+$14, newcluster>>16)
else
brwclust(readbytec(writelink), newcluster)
writelink := fatptr + i
fclust := newcluster
frem := SECTORSIZE << clustershift
quit
else
cluststart += (SECTORSIZE >> filesystem)
if (cluststart => totclusters)
cluststart := 0
count--
if (rcnt < 0)
rcnt := -5 ' No space left on device
quit
if (frem => SECTORSIZE)
sdspi.writeblock(datablock, @buf)
if (rcnt == SECTORSIZE) ' full buffer, clear it
floc += rcnt
frem -= rcnt
bufat := 0
bufend := rcnt
if (rcnt < 0 or metadata) ' update metadata even if error
readblockc(direntry >> SECTORSHIFT) ' flushes unwritten FAT too
brwlong(@buf2+(direntry & (SECTORSIZE-filesystem))+$1c, floc+bufat)
flushifdirty
if (rcnt < 0)
abort(rcnt)
return rcnt
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
frem := (SECTORSIZE << clustershift) <# (filesize - floc)
sdspi.readblock(datablock, @buf)
r := SECTORSIZE
if (floc + r => filesize)
r := filesize - floc
floc += r
frem -= r
bufat := 0
bufend := r
' return r (default return)
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.
}}
if (direntry)
r := pflush
bufat := 0
bufend := 0
filesize := 0
floc := 0
frem := 0
writelink := 0
direntry := 0
fclust := 0
' return r (default return)
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 (July 12, 2009). You can change this
' to return a valid date/time if you have access to this data in
' your setup.
}}
return constant(((2009-1980) << 25) + (1 << 21) + (12 << 16) + (7 << 11))
pub popen(s, mode) : r | 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.
}}
pclose
i := 0
repeat while (i<8 and byte[s] and byte[s] <> ".")
padname[i++] := uc(byte[s++])
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++] := " "
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
if (padname[i] <> byte[s][i])
quit
if (i == 11 and 0 == (byte[s][$0b] & $18)) ' this always returns
fclust := brword(s+$1a)
if (filesystem == 2)
fclust += brword(s+$14) << 16
filesize := brlong(s+$1c)
if (mode == "r")
frem := (SECTORSIZE << clustershift) <# (filesize)
return 0
if (byte[s][11] & $d9)
abort(-6) ' no permission to write
if (mode == "d")
brwword(s, $e5)
if (fclust)
freeclusters(fclust)
flushifdirty
return 0
if (mode == "w")
brwword(s+$1a, 0)
brwword(s+$14, 0)
brwlong(s+$1c, 0)
writelink := 0
direntry := dirptr
if (fclust)
freeclusters(fclust)
bufend := SECTORSIZE
fclust := 0
filesize := 0
frem := 0
return 0
elseif (mode == "a")
' this code will eventually be moved to seek
frem := filesize
freeentry := SECTORSIZE << clustershift
if (fclust => endofchain)
fclust := 0
repeat while (frem > freeentry)
if (fclust < 2)
abort(-7) ' eof repeat while following chain
fclust := nextcluster
frem -= freeentry
floc := filesize & constant(!(SECTORSIZE - 1))
bufend := SECTORSIZE
bufat := frem & constant(SECTORSIZE - 1)
writelink := 0
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
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+$1a, 0)
brwword(s+$14, 0)
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 := 0
fclust := 0
bufend := SECTORSIZE
' return r (default return)
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.
}}
repeat while (count > 0)
if (bufat => bufend)
t := pfillbuf
if (t =< 0)
if (r > 0)
' parens below prevent this from being optimized out
return (r)
return t
t := (bufend - bufat) <# (count)
if ((t | (ubuf) | bufat) & 3)
bytemove(ubuf, @buf+bufat, t)
else
longmove(ubuf, @buf+bufat, t>>2)
bufat += t
r += t
ubuf += t
count -= t
' return r (default return)
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 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)
return -1
bufat += DIRSIZE
if (byte[at] <> $e5 and (byte[at][$0b] & $18) == 0)
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
{{
' Utility routines; may be removed.
}}

{{
' 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.
}}

+ 1
- 0
src/mb_spi.spin
File diff suppressed because it is too large
View File


BIN
src/mouse.spin View File


BIN
src/simple_numbers.spin View File


BIN
src/vga_hires_text.spin View File


Loading…
Cancel
Save