diff --git a/Apps/Source/applvers.h b/Apps/Source/applvers.h index a6184bcd..2e3f88af 100644 --- a/Apps/Source/applvers.h +++ b/Apps/Source/applvers.h @@ -1,14 +1,14 @@ /************************************/ -/* applvers.h dwg - 2.5.3.19 */ +/* applvers.h dwg - 2.5.4.20 */ /************************************/ #define A_RMJ 2 #define A_RMN 5 -#define A_RUP 3 -#define A_RTP 19 +#define A_RUP 4 +#define A_RTP 20 -#define A_MONTH 1 -#define A_DAY 20 +#define A_MONTH 3 +#define A_DAY 1 #define A_YEAR 2014 #define A_YR 14 diff --git a/Apps/Source/applvers.lib b/Apps/Source/applvers.lib index c37f1454..46c3d81b 100644 --- a/Apps/Source/applvers.lib +++ b/Apps/Source/applvers.lib @@ -1,16 +1,16 @@ -; applvers.lib 3/31/2012 dwg - For RomWBW 2.5.0.14 Release +; applvers.lib 3/31/2012 dwg - For RomWBW 2.5.4.20 Release A$RMJ equ 2 A$RMN equ 5 -A$RUP equ 3 -A$RTP equ 19 +A$RUP equ 4 +A$RTP equ 20 -A$MONTH equ 1 -A$DAY equ 20 +A$MONTH equ 3 +A$DAY equ 1 A$YEAR equ 2014 date macro -dat db ' 1/20/2014$' +dat db ' 3/1/2014$' endm serial macro diff --git a/Doc/ChangeLog.txt b/Doc/ChangeLog.txt index f5ffff1c..0148c3c0 100644 --- a/Doc/ChangeLog.txt +++ b/Doc/ChangeLog.txt @@ -1,3 +1,8 @@ +Version 2.5.4 +------------- +- WBW: Fixed IOBYTE handling +- WBW: Added Propeller firmware support for PropIO V2 + Version 2.5.3 ------------- - WBW: Fixed EXM in DPB for 360K floppy definition (credit to Martin for finding this error) diff --git a/ReadMe.txt b/ReadMe.txt index b33ba6c3..1c191555 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -8,8 +8,8 @@ Builders: Wayne Warthen (wwarthen@gmail.com) Douglas Goodall (douglas_goodall@mac.com) David Giles (vk5dg@internode.on.net) -Updated: 2014-01-20 -Version: 2.5.3 +Updated: 2014-03-01 +Version: 2.5.4 This is an adaptation of CP/M-80 2.2 and ZSDOS/ZCPR targeting ROMs for all N8VEM Z80 hardware variations diff --git a/Source/cbios.asm b/Source/cbios.asm index 8c1b7188..874916d7 100644 --- a/Source/cbios.asm +++ b/Source/cbios.asm @@ -1792,18 +1792,24 @@ DSK_CNT .EQU DPH_CNT ; ; MAP LOGICAL TO PHYSICAL DEVICES ; -LD_TTY .EQU DEFCON +#IF (PLATFORM == PLT_N8) +TTYDEV .EQU CIODEV_ASCI +#ELSE +TTYDEV .EQU CIODEV_UART +#ENDIF +; +LD_TTY .EQU TTYDEV LD_CRT .EQU CIODEV_CRT LD_BAT .EQU CIODEV_BAT -LD_UC1 .EQU DEFCON -LD_PTR .EQU DEFCON -LD_UR1 .EQU DEFCON -LD_UR2 .EQU DEFCON -LD_PTP .EQU DEFCON -LD_UP1 .EQU DEFCON -LD_UP2 .EQU DEFCON -LD_LPT .EQU DEFCON -LD_UL1 .EQU DEFCON +LD_UC1 .EQU TTYDEV +LD_PTR .EQU TTYDEV +LD_UR1 .EQU TTYDEV +LD_UR2 .EQU TTYDEV +LD_PTP .EQU TTYDEV +LD_UP1 .EQU TTYDEV +LD_UP2 .EQU TTYDEV +LD_LPT .EQU TTYDEV +LD_UL1 .EQU TTYDEV ; #IF (PLATFORM == PLT_N8) LD_UC1 .SET CIODEV_ASCI + 1 diff --git a/Source/diskdefs b/Source/diskdefs index 2e1f5725..b196ace7 100644 --- a/Source/diskdefs +++ b/Source/diskdefs @@ -266,24 +266,24 @@ diskdef interak sectrk 20 blocksize 4096 maxdir 256 - skew 1 + skew 0 boottrk 2 os 2.2 end -# For 512KB ROM, less 32K for system image +# For 512KB ROM, less 64K reserved for system diskdef rom512KB seclen 128 tracks 14 sectrk 256 blocksize 2048 maxdir 256 - skew 1 + skew 0 boottrk 0 os 2.2 end -# For 1024KB ROM, less 32K for system image +# For 1024KB ROM, less 64K reserved for system diskdef rom1024KB seclen 128 tracks 30 @@ -295,47 +295,95 @@ diskdef rom1024KB os 2.2 end -# For N8VEM mass storage (4 raw partitions) -diskdef hd0 +# N8VEM Hard Disks, hd0 - hd3 are first 4 slices of disk +diskdef n8vem_hd0 seclen 512 tracks 65 sectrk 256 blocksize 4096 maxdir 512 - skew 1 + skew 0 boottrk 1 os 2.2 end -diskdef hd1 +diskdef n8vem_hd1 seclen 512 tracks 130 sectrk 256 blocksize 4096 maxdir 512 - skew 1 + skew 0 boottrk 66 os 2.2 end -diskdef hd2 +diskdef n8vem_hd2 seclen 512 tracks 195 sectrk 256 blocksize 4096 maxdir 512 - skew 1 + skew 0 boottrk 131 os 2.2 end -diskdef hd3 +diskdef n8vem_hd3 seclen 512 tracks 260 sectrk 256 blocksize 4096 maxdir 512 - skew 1 + skew 0 boottrk 196 os 2.2 end + +# N8VEM 720K floppy media +diskdef n8vem_fd720 + seclen 512 + tracks 160 + sectrk 9 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 4 + os 2.2 +end + +# N8VEM 1.44M floppy media +diskdef n8vem_fd144 + seclen 512 + tracks 160 + sectrk 18 + blocksize 2048 + maxdir 256 + skew 0 + boottrk 2 + os 2.2 +end + +# N8VEM 360K floppy media +diskdef n8vem_fd360 + seclen 512 + tracks 80 + sectrk 9 + blocksize 2048 + maxdir 128 + skew 0 + boottrk 4 + os 2.2 +end + +# N8VEM 1.20M floppy media +diskdef n8vem_fd120 + seclen 512 + tracks 160 + sectrk 15 + blocksize 2048 + maxdir 256 + skew 0 + boottrk 2 + os 2.2 +end diff --git a/Source/ver.inc b/Source/ver.inc index cdb4441e..eda7a5eb 100644 --- a/Source/ver.inc +++ b/Source/ver.inc @@ -1,7 +1,7 @@ #DEFINE RMJ 2 #DEFINE RMN 5 -#DEFINE RUP 3 -#DEFINE RTP 19 -#DEFINE BIOSVER "2.5.3" -#DEFINE BIOSBLD "Build 19" +#DEFINE RUP 4 +#DEFINE RTP 20 +#DEFINE BIOSVER "2.5.4" +#DEFINE BIOSBLD "Build 20" #DEFINE REVISION 500 diff --git a/Support/PropIO/PropIO.eeprom b/Support/PropIO/PropIO.eeprom index f872a15e..f789241a 100644 Binary files a/Support/PropIO/PropIO.eeprom and b/Support/PropIO/PropIO.eeprom differ diff --git a/Support/PropIO/Spin/PropIO.spin b/Support/PropIO/Spin/PropIO.spin index 6cebfda2..21ba18c3 100644 Binary files a/Support/PropIO/Spin/PropIO.spin and b/Support/PropIO/Spin/PropIO.spin differ diff --git a/Support/PropIO/Spin/VGA_1024.spin b/Support/PropIO/Spin/VGA_1024.spin index 96ea49a7..82d12261 100644 --- a/Support/PropIO/Spin/VGA_1024.spin +++ b/Support/PropIO/Spin/VGA_1024.spin @@ -131,7 +131,7 @@ PUB cls1(c,screencolor,pcport,ascii,CR) | i,x,y inverse := 1 - statprint(36,0, string(" N8VEM ParPortProp | RomWBW v0.93")) + statprint(36,0, string(" N8VEM PropIO | RomWBW v0.94")) inverse := 0 statprint(37,0, string(" ")) statprint(38,0, string(" ")) diff --git a/Support/PropIO2/PropIO2.eeprom b/Support/PropIO2/PropIO2.eeprom new file mode 100644 index 00000000..1d536977 Binary files /dev/null and b/Support/PropIO2/PropIO2.eeprom differ diff --git a/Support/PropIO2/Spin/E555_SPKEngine.spin b/Support/PropIO2/Spin/E555_SPKEngine.spin new file mode 100644 index 00000000..02e55e87 --- /dev/null +++ b/Support/PropIO2/Spin/E555_SPKEngine.spin @@ -0,0 +1 @@ +{{ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // E555 Speaker Engine // // Author: Kwabena W. Agyeman // Updated: 7/27/2010 // Designed For: P8X32A // Version: 1.1 // // Copyright (c) 2010 Kwabena W. Agyeman // See end of file for terms of use. // // Update History: // // v1.0 - Original release - 8/26/2009. // v1.1 - Added support for variable pin assignments - 7/27/2010. // // For each included copy of this object only one spin interpreter should access it at a time. // // Nyamekye, /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Speaker Circuit: // // SpeakerPinNumber --- Speaker Driver (Active High). // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }} PUB speakerFrequency(newFrequency, speakerPinNumber) '' 10 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Changes the speaker frequency using the SPIN interpreter's counter modules. '' // '' // NewFrequency - The new frequency. Between 0 Hz and 80MHz @ 80MHz. -1 to reset the pin and counter modules. '' // SpeakerPinNumber - Pin to use to drive the speaker circuit. Between 0 and 31. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// speakerSetup((newFrequency <> -1), speakerPinNumber) newFrequency := ((newFrequency <# clkfreq) #> 0) result := 1 repeat 32 newFrequency <<= 1 result <-= 1 if(newFrequency => clkfreq) newFrequency -= clkfreq result += 1 frqa := result~ phsb := 0 PUB speakerVolume(newVolume, speakerPinNumber) '' 10 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Changes the speaker volume using the SPIN interpreter's counter modules. '' // '' // NewVolume - The new volume. Between 0% and 100%. -1 to reset the pin and counter modules. '' // SpeakerPinNumber - Pin to use to drive the speaker circuit. Between 0 and 31. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// speakerSetup((newVolume <> -1), speakerPinNumber) frqb := (((100 - ((newVolume <# 100) #> 0)) * constant(posx / 50)) | $7) PRI speakerSetup(activeOrInactive, speakerPinNumber) ' 5 Stack Longs speakerPinNumber := ((speakerPinNumber <# 31) #> 0) dira[speakerPinNumber] := activeOrInactive outa[speakerPinNumber] := false ctra := ((constant(%0_0100 << 26) + speakerPinNumber) & activeOrInactive) ctrb := ((constant(%0_0110 << 26) + speakerPinNumber) & activeOrInactive) {{ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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/Support/PropIO2/Spin/Keyboard.spin b/Support/PropIO2/Spin/Keyboard.spin new file mode 100644 index 00000000..67d8b081 Binary files /dev/null and b/Support/PropIO2/Spin/Keyboard.spin differ diff --git a/Support/PropIO2/Spin/Parallax Serial Terminal.spin b/Support/PropIO2/Spin/Parallax Serial Terminal.spin new file mode 100644 index 00000000..a525a4be Binary files /dev/null and b/Support/PropIO2/Spin/Parallax Serial Terminal.spin differ diff --git a/Support/PropIO2/Spin/PropIO2.spin b/Support/PropIO2/Spin/PropIO2.spin new file mode 100644 index 00000000..9c180f14 Binary files /dev/null and b/Support/PropIO2/Spin/PropIO2.spin differ diff --git a/Support/PropIO2/Spin/VGA_1024.spin b/Support/PropIO2/Spin/VGA_1024.spin new file mode 100644 index 00000000..9f3c2851 --- /dev/null +++ b/Support/PropIO2/Spin/VGA_1024.spin @@ -0,0 +1,704 @@ +'' VGA_1024.spin +'' +'' MODIFIED BY VINCE BRIEL FOR POCKETERM FEATURES +'' MODIIFED BY JEFF LEDGER / AKA OLDBITCOLLECTOR +'' + +CON + cols = 80 '128 ' number of screen columns + lcols = cols / 4 ' number of long in columns + rows = 40 '64 ' number of screen rows + chars = rows*cols ' number of screen characters + esc = $CB ' keyboard esc char + rowsnow = 36 ' adjusted for split screen effect + maxChars = rowsnow*cols ' adjusted value for split screen effect + lastChar = maxChars / 4 ' last screen position in longs adjusted for split + lastLine = (rowsnow - 1) * cols ' character position of last row + cols1 = 81 ' adjusted value for 80th character + TURQUOISE = $29 + +OBJ + vga : "vga_Hires_Text" + +VAR + byte screen[chars] ' screen character buffer + byte tmpl[cols] ' temporary line buffer + word colors[rows] ' color specs for each screen row (see ColorPtr description above) + byte cursor[6] ' cursor info array (see CursorPtr description above) + long sync, loc, xloc, yloc ' sync used by VGA routine, others are local screen pointers + long kbdreq ' global val of kbdflag + long BR[8] + long Brate + byte inverse + byte invs + byte state ' Current state of state machine + word pos ' Current Position on the screen + word oldpos ' Previous location of cursor before update + word regionTop, regionBot ' Scroll region top/bottom + long arg0 ' First argument of escape sequence + long arg1 ' Second argument of escape sequence + byte lastc ' Last displayed char + word statpos + long vgabasepin + +PUB start(BasePin) | i, char + vgabasepin := BasePin + +''init screen colors to gold on blue + repeat i from 0 to rows - 1 + colors[i] := $08F0 '$2804 (if you want cyan on blue) + +''init cursor attributes + cursor[2] := %110 ' init cursor to underscore with slow blink + BR[0]:=300 + BR[1]:=1200 + BR[2]:=2400 + BR[3]:=4800 + BR[4]:=9600 + BR[5]:=19200 + BR[6]:=38400 + BR[7]:=57600 + BR[8]:=115200 + xloc := cursor[0] := 0 + yloc := cursor[1] := 0 + loc := xloc + yloc*cols + + pos := 0 + regionTop := 0 + regionBot := 35 * cols + state := 0 + statpos := 37 * cols + +PUB vidon + if (!vga.start(vgabasepin, @screen, @colors, @cursor, @sync)) + return false + + waitcnt(clkfreq * 1 + cnt) 'wait 1 second for cogs to start + + +PUB vidoff + vga.stop + + +PUB inv(c) + inverse:=c + +PUB color(colorVal) | i + repeat i from 0 to rows - 1 + colors[i] := $0000 | colorVal + +PUB cursorset(c) | i + i:=%000 + if c == 1 + i:= %001 + if c == 2 + i:= %010 + if c == 3 + i:= %011 + if c == 4 + i:= %101 + if c == 5 + i:= %110 + if c == 6 + i:= %111 + if c == 7 + i:= %000 + cursor[2] := i + +PUB bin(value, digits) + +'' Print a binary number, specify number of digits + + repeat while digits > 32 + outc("0") + digits-- + + value <<= 32 - digits + + repeat digits + outc((value <-= 1) & 1 + "0") + + +PUB clrbtm(ColorVal) | i + repeat i from 36 to rows - 1 'was 35 + colors[i] := $0000 + ColorVal + +PUB cls1(c,screencolor,pcport,ascii,CR) | i,x,y + + longfill(@screen[0], $20202020, chars / 4) + + clrbtm(TURQUOISE) + + inverse := 1 + + statprint(36,0, string(" N8VEM PropIO V2 | RomWBW v0.94")) + inverse := 0 + statprint(37,0, string(" ")) + statprint(38,0, string(" ")) + statprint(39,0, string(" ")) + + +{{ + x :=xloc + y := yloc + invs := inverse + ''clrbtm(TURQUOISE) + longfill(@screen, $20202020, chars/4) + xloc := 0 + yloc :=0 + loc := xloc + yloc*cols + repeat 80 + outc(32) + xloc := 0 + yloc :=36 + loc := xloc + yloc*cols + inverse := 1 + str(string(" propIO V 0.91 ")) + inverse := 0 + str(string("Baud Rate: ")) + i:= BR[6] + dec(i) + str(string(" ")) + xloc := 18 + loc := xloc + yloc*cols + str(string("Color ")) + str(string("PC Port: ")) + if pcport == 1 + str(string("OFF ")) + if pcport == 0 + str(string("ON ")) + str(string(" Force 7 bit: ")) + if ascii == 0 + str(string("NO ")) + if ascii == 1 + str(string("YES ")) + str(string(" Cursor CR W/LF: ")) + if CR == 1 + str(string("YES")) + if CR == 0 + str(string("NO ")) + outc(13) + outc(10) + + inverse:=1 + xloc := 6 + loc := xloc + yloc*cols + str(string("F1")) + xloc := 19 + loc := xloc + yloc*cols + str(string("F2")) + xloc := 30 + loc := xloc + yloc*cols + str(string("F3")) + xloc := 46 + loc := xloc + yloc*cols + str(string("F4")) + xloc := 58 + loc := xloc + yloc*cols + str(string("F5")) + xloc := 70 + loc := xloc + yloc*cols + str(string("F6")) + inverse := invs + xloc := cursor[0] := x 'right & left was 0 + yloc := cursor[1] := y 'from top was 1 + loc := xloc + yloc*cols +}} + +PUB clsupdate(c,screencolor,PCPORT,ascii,CR) | i,x,y,locold + + invs := inverse + locold := loc + x := xloc + y := yloc + ''(TURQUOISE) + xloc := 0 + yloc :=36 + loc := xloc + yloc*cols + inverse := 1 + str(string(" propIO V 0.81 ")) + inverse := 0 + xloc := 0 + yloc :=37 + loc := xloc + yloc*cols + str(string("Baud Rate: ")) + i:= BR[6] + dec(i) + str(string(" ")) + xloc := 18 + loc := xloc + yloc*cols + + str(string("Color ")) + str(string("PC Port: ")) + if pcport == 1 + str(string("OFF ")) + if pcport == 0 + str(string("ON ")) + str(string(" Force 7 bit: ")) + if ascii == 0 + str(string("NO ")) + if ascii == 1 + str(string("YES ")) + str(string(" Cursor CR W/LF: ")) + if CR == 1 + str(string("YES")) + if CR == 0 + str(string("NO ")) + xloc := 0 + yloc :=38 + loc := xloc + yloc*cols + inverse:=1 + xloc := 6 + loc := xloc + yloc*cols + str(string("F1")) + xloc := 19 + loc := xloc + yloc*cols + str(string("F2")) + xloc := 30 + loc := xloc + yloc*cols + str(string("F3")) + xloc := 46 + loc := xloc + yloc*cols + str(string("F4")) + xloc := 58 + loc := xloc + yloc*cols + str(string("F5")) + xloc := 70 + loc := xloc + yloc*cols + str(string("F6")) + inverse := invs + xloc := cursor[0] := x + yloc := cursor[1] := y +' loc := xloc + yloc*cols + loc := locold + +PUB dec(value) | i + +'' Print a decimal number + + if value < 0 + -value + outc("-") + + i := 1_000_000_000 + + repeat 10 + if value => i + outc(value/i + "0") + value //= i + result~~ + elseif result or i == 1 + outc("0") + i /= 10 + +PUB hex(value, digits) + +'' Print a hexadecimal number, specify number of digits + + repeat while digits > 8 + outc("0") + digits-- + + value <<= (8 - digits) << 2 + + repeat digits + outc(lookupz((value <-= 4) & $f : "0".."9", "A".."F")) + + +PUB str(string_ptr) + +'' Print a zero terminated string + + repeat strsize(string_ptr) + process_char(byte[string_ptr++]) + +PUB statprint(r, c, str1) | x, ptr + + ptr := r * cols + c + repeat x from 0 to STRSIZE(str1) - 1 + putc(ptr++, BYTE[str1 + x]) + +PUB statnum(r, c, num1) | i, ptr + + ptr := r * cols + c + + if num1 < 0 + -num1 + putc(ptr++,"-") + + i := 1_000_000_000 + + repeat 10 + if num1 => i + putc(ptr++, (num1/i +"0")) + num1 //= i + result~~ + elseif result or i == 1 + putc(ptr++, "0") + i /= 10 + +PUB putc(position, c) + if inverse + c |= $80 + screen[position] := c + +PUB cls + longfill (@screen, $20202020, lastChar) + +PUB fullcls + longfill(@screen, $20202020, 800) + +PUB setInverse(val) + inverse := val + +PUB setInv(c) + if c == 7 + setInverse(1) + else + setInverse(0) + +PUB clEOL(position) | count + count := cols - (position // cols) + bytefill(@screen + position, $20, count) + +PUB clBOL(position) | count + count := position // cols + bytefill(@screen + position - count, $20, count) + +PUB delLine(position) | src, count + position -= position // cols + + src := position + cols + + count := (maxChars - src) / 4 + + if count > 0 + longmove(@screen + position, @screen + src, count) + + longfill(@screen + lastLine, $20202020, lcols) + +PUB clEOS(position) + cleol(position) + position += cols - (position // cols) + repeat while position < maxChars + longfill(@screen + position, $20202020, lcols) + pos += cols + +PUB setCursorPos(position) + cursor[0] := position // cols + cursor[1] := position / cols + +PUB insLine(position) | base, nxt + base := position - (position // cols) + position := lastLine + repeat while position > base + nxt := position - cols + longmove(@screen + position, @screen + nxt, lcols) + position := nxt + clEOL(base) + +PUB insChar(position) | count + count := (cols - (position // cols)) - 1 + bytemove(@tmpl, @screen + position, count) + screen[position] := " " + bytemove(@screen + position + 1, @tmpl, count) + +PUB delChar(position) | count + count := (cols - (position // cols)) - 1 + bytemove(@screen + position, @screen + position + 1, count) + screen[position + count] := " " + +PRI inRegion : answer + answer := (pos => regionTop) AND (pos < regionBot) + +PRI scrollUp + delLine(regionTop) + if regionBot < maxChars + insLine(regionBot) + +PRI scrollDown + if regionBot < maxChars + delLine(regionBot) + insLine(regionTop) + +PRI ansi(c) | x, defVal + + state := 0 + + if (c <> "r") AND (c <> "J") AND (c <> "m") AND (c <> "K") + if arg0 == -1 + arg0 := 1 + if arg1 == -1 + arg1 := 1 + + case c + "@": + repeat while arg0-- > 0 + insChar(pos) + + "b": + repeat while arg0-- > 0 + outc(lastc) + + "d": + if (arg0 < 1) OR (arg0 > rows) + arg0 := rows + pos := ((arg0 - 1) * cols) + (pos // cols) + + "m": + setInv(arg0) + if arg1 <> -1 + setInv(arg1) + + "r": + if arg0 < 1 + arg0 := 1 + elseif arg0 > cols + arg0 := cols + if arg1 < 1 + arg1 := 1 + elseif arg1 > cols + arg1 := cols + if arg1 < arg0 + arg1 := arg0 + + regionTop := (arg0 - 1) * cols + regionBot := arg1 * cols + pos := 0 + + "A": + repeat while arg0-- > 0 + pos -= cols + if pos < 0 + pos += cols + return + + "B": + repeat while arg0-- > 0 + pos += cols + if pos => maxChars + pos -= cols + return + + "C": + repeat while arg0-- > 0 + pos += 1 + if pos => maxChars + pos -= 1 + return + + "D": + repeat while arg0-- > 0 + pos -= 1 + if pos < 0 + pos := 0 + return + + "G": + if (arg0 < 1) OR (arg0 > cols) + arg0 := cols + pos := (pos - (pos // cols)) + (arg0 - 1) + + "H", "f": + if arg0 =< 0 + arg0 := 1 + if arg1 =< 0 + arg1 := 1 + pos := (cols * (arg0 - 1)) + (arg1 - 1) + if pos < 0 + pos := 0 + if pos => maxChars + pos := maxChars - 1 + + "J": + if arg0 == 1 + clBOL(pos) + x := pos - cols + x -= x // cols + repeat while x => 0 + clEOL(x) + x -= cols + return + + if arg0 == 2 + pos := 0 + + clEOL(pos) + x := pos + cols + x -= (x // cols) + repeat while x < maxChars + clEOL(x) + x += cols + + "K": + if arg0 == -1 + clEOL(pos) + elseif arg0 == 1 + clBOL(pos) + else + clEOL(pos - (pos // cols)) + + "L": + if inRegion + repeat while arg0-- > 0 + if regionBot < maxChars + delLine(regionBot) + insLine(pos) + + "M": + if inRegion + repeat while arg0-- > 0 + delLine(pos) + if regionBot < maxChars + insLine(regionBot) + + "P": + repeat while arg0-- + delChar(pos) + +PRI outc(c) + + putc(pos++, lastc := c) + if pos == regionBot + scrollUp + pos -= cols + elseif pos == maxChars + pos := lastLine + +PUB process_char(c) + + case state + + 0: + if c > 127 + c := $20 + + if c => $20 + outc(c) + setCursorPos(pos) + return + + if c == $1B + state := 1 + return + + if c == $0D + pos := pos - (pos // cols) + setCursorPos(pos) + return + + if c == $0A + if inRegion + pos += cols + if pos => regionBot + scrollUp + pos -= cols + else + pos += cols + if pos => maxChars + pos -= cols + setCursorPos(pos) + return + + if c == 9 + pos += (8 - (pos // 8)) + + if pos => maxChars + pos := lastLine + delLine(0) + + setCursorPos(pos) + return + + if c == 8 + if pos > 0 + pos -= 1 + setCursorPos(pos) + return + + 1: + case c + "[": + arg0 := arg1 := -1 + state := 2 + return + + "P": + pos += cols + if pos => maxChars + pos -= cols + + "K": + if pos > 0 + pos -= 1 + + "H": + pos -= cols + if pos < 0 + pos += cols + + "D": + if inRegion + scrollUp + + "M": + if inRegion + scrollDown + + "G": + pos := 0 + + "(": + state := 5 + return + + state := 0 + return + + 2: + if (c => "0") AND (c =< "9") + if arg0 == -1 + arg0 := c - "0" + else + arg0 := (arg0 * 10) + (c - "0") + return + + if c == ";" + state := 3 + return + + ansi(c) + setCursorPos(pos) + return + + 3: + if (c => "0") AND (c =< "9") + if arg1 == -1 + arg1 := c - "0" + else + arg1 := (arg1 * 10) + (c - "0") + return + + if c == ";" + state := 4 + return + + ansi(c) + setCursorPos(pos) + return + + 4: + if (c => "0") AND (c =< "9") + return + + if c == ";" + return + ansi(c) + setCursorPos(pos) + return + + 5: + state := 0 + return + + return \ No newline at end of file diff --git a/Support/PropIO2/Spin/VGA_HiRes_Text.spin b/Support/PropIO2/Spin/VGA_HiRes_Text.spin new file mode 100644 index 00000000..5b892316 Binary files /dev/null and b/Support/PropIO2/Spin/VGA_HiRes_Text.spin differ diff --git a/Support/PropIO2/Spin/safe_spi.spin b/Support/PropIO2/Spin/safe_spi.spin new file mode 100644 index 00000000..a63cb229 --- /dev/null +++ b/Support/PropIO2/Spin/safe_spi.spin @@ -0,0 +1,920 @@ +{{ + SPI interface routines for SD & SDHC & MMC cards + + Jonathan "lonesock" Dummer + version 0.3.0 2009 July 19 + + Using multiblock SPI mode exclusively. + + This is the "SAFE" version...uses + * 1 instruction per bit writes + * 2 instructions per bit reads + + For the fsrw project: + fsrw.sf.net +}} + +CON + ' possible card types + type_MMC = 1 + type_SD = 2 + type_SDHC = 3 + + ' Error codes + ERR_CARD_NOT_RESET = -1 + ERR_3v3_NOT_SUPPORTED = -2 + ERR_OCR_FAILED = -3 + ERR_BLOCK_NOT_LONG_ALIGNED = -4 + '... + ' These errors are for the assembly engine...they are negated inside, and need to be <= 511 + ERR_ASM_NO_READ_TOKEN = 100 + ERR_ASM_BLOCK_NOT_WRITTEN = 101 + ' NOTE: errors -128 to -255 are reserved for reporting R1 response errors + '... + ERR_SPI_ENGINE_NOT_RUNNING = -999 + ERR_CARD_BUSY_TIMEOUT = -1000 + + ' SDHC/SD/MMC command set for SPI + CMD0 = $40+0 ' GO_IDLE_STATE + CMD1 = $40+1 ' SEND_OP_COND (MMC) + ACMD41 = $C0+41 ' SEND_OP_COND (SDC) + CMD8 = $40+8 ' SEND_IF_COND + CMD9 = $40+9 ' SEND_CSD + CMD10 = $40+10 ' SEND_CID + CMD12 = $40+12 ' STOP_TRANSMISSION + CMD13 = $40+13 ' SEND_STATUS + ACMD13 = $C0+13 ' SD_STATUS (SDC) + CMD16 = $40+16 ' SET_BLOCKLEN + CMD17 = $40+17 ' READ_SINGLE_BLOCK + CMD18 = $40+18 ' READ_MULTIPLE_BLOCK + CMD23 = $40+23 ' SET_BLOCK_COUNT (MMC) + ACMD23 = $C0+23 ' SET_WR_BLK_ERASE_COUNT (SDC) + CMD24 = $40+24 ' WRITE_BLOCK + CMD25 = $40+25 ' WRITE_MULTIPLE_BLOCK + CMD55 = $40+55 ' APP_CMD + CMD58 = $40+58 ' READ_OCR + CMD59 = $40+59 ' CRC_ON_OFF + + ' buffer size for my debug cmd log + 'LOG_SIZE = 256<<1 + +{ +VAR + long SPI_engine_cog + ' these are used for interfacing with the assembly engine | temporary initialization usage + long SPI_command ' "t", "r", "w", 0 =>done, <0 => error | pin mask + long SPI_block_index ' which 512-byte block to read/write | cnt at init + long SPI_buffer_address ' where to get/put the data in Hub RAM | unused +'} +DAT +'' I'm placing these variables in a DAT section to make this driver a singleton. +'' If for some reason you really need more than one driver (e.g. if you have more +'' than a single SD socket), move these back into VAR. +SPI_engine_cog long 0 +' these are used for interfacing with the assembly engine | temporary initialization usage +SPI_command long 0 ' "t", "r", "w", 0 =>done, <0 => error | unused +SPI_block_index long 0 ' which 512-byte block to read/write | cnt at init +SPI_buffer_address long 0 ' where to get/put the data in Hub RAM | unused + +{ +VAR + ' for debug ONLY + byte log_cmd_resp[LOG_SIZE+1] +PUB get_log_pointer + return @log_cmd_resp +'} + +PUB start( basepin ) +{{ + This is a compatibility wrapper, and requires that the pins be + both consecutive, and in the order DO CLK DI CS. +}} + return start_explicit( basepin, basepin+1, basepin+2, basepin+3 ) + +PUB readblock( block_index, buffer_address ) + if SPI_engine_cog == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + if (buffer_address & 3) + abort ERR_BLOCK_NOT_LONG_ALIGNED + SPI_block_index := block_index + SPI_buffer_address := buffer_address + SPI_command := "r" + repeat while SPI_command == "r" + if SPI_command < 0 + abort SPI_command + +PUB writeblock( block_index, buffer_address ) + if SPI_engine_cog == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + if (buffer_address & 3) + abort ERR_BLOCK_NOT_LONG_ALIGNED + SPI_block_index := block_index + SPI_buffer_address := buffer_address + SPI_command := "w" + repeat while SPI_command == "w" + if SPI_command < 0 + abort SPI_command + +PUB get_seconds + if SPI_engine_cog == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + SPI_command := "t" + repeat while SPI_command == "t" + ' secods are in SPI_block_index, remainder is in SPI_buffer_address + return SPI_block_index + +PUB get_milliseconds : ms + if SPI_engine_cog == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + SPI_command := "t" + repeat while SPI_command == "t" + ' secods are in SPI_block_index, remainder is in SPI_buffer_address + ms := SPI_block_index * 1000 + ms += SPI_buffer_address * 1000 / clkfreq + +PUB start_explicit( DO, CLK, DI, CS ) : card_type | tmp, i +{{ + Do all of the card initialization in SPIN, then hand off the pin + information to the assembly cog for hot SPI block R/W action! +}} + ' Start from scratch + stop + ' clear my log buffer + { + bytefill( @log_cmd_resp, 0, LOG_SIZE+1 ) + dbg_ptr := @log_cmd_resp + dbg_end := dbg_ptr + LOG_SIZE + '} + ' wait ~4 milliseconds + waitcnt( 500 + (clkfreq>>8) + cnt ) + ' (start with cog variables, _BEFORE_ loading the cog) + pinDO := DO + maskDO := |< DO + pinCLK := CLK + pinDI := DI + maskDI := |< DI + maskCS := |< CS + adrShift := 9 ' block = 512 * index, and 512 = 1<<9 + ' pass the output pin mask via the command register + maskAll := maskCS | (| 74 clocks + outa |= maskAll + repeat 4096 + outa[CLK]~~ + outa[CLK]~ + ' time-hack + SPI_block_index := cnt + ' reset the card + tmp~ + repeat i from 0 to 9 + if tmp <> 1 + tmp := send_cmd_slow( CMD0, 0, $95 ) + if (tmp & 4) + ' the card said CMD0 ("go idle") was invalid, so we're possibly stuck in read or write mode + if i & 1 + ' exit multiblock read mode + repeat 4 + read_32_slow ' these extra clocks are required for some MMC cards + send_slow( $FD, 8 ) ' stop token + read_32_slow + repeat while read_slow <> $FF + else + ' exit multiblock read mode + send_cmd_slow( CMD12, 0, $61 ) + if tmp <> 1 + ' the reset command failed! + crash( ERR_CARD_NOT_RESET ) + ' Is this a SD type 2 card? + if send_cmd_slow( CMD8, $1AA, $87 ) == 1 + ' Type2 SD, check to see if it's a SDHC card + tmp := read_32_slow + ' check the supported voltage + if (tmp & $1FF) <> $1AA + crash( ERR_3v3_NOT_SUPPORTED ) + ' try to initialize the type 2 card with the High Capacity bit + repeat while send_cmd_slow( ACMD41, |<30, $77 ) + ' the card is initialized, let's read back the High Capacity bit + if send_cmd_slow( CMD58, 0, $FD ) <> 0 + crash( ERR_OCR_FAILED ) + ' get back the data + tmp := read_32_slow + ' check the bit + if tmp & |<30 + card_type := type_SDHC + adrShift := 0 + else + card_type := type_SD + else + ' Either a type 1 SD card, or it's MMC, try SD 1st + if send_cmd_slow( ACMD41, 0, $E5 ) < 2 + ' this is a type 1 SD card (1 means busy, 0 means done initializing) + card_type := type_SD + repeat while send_cmd_slow( ACMD41, 0, $E5 ) + else + ' mark that it's MMC, and try to initialize + card_type := type_MMC + repeat while send_cmd_slow( CMD1, 0, $F9 ) + ' some SD or MMC cards may have the wrong block size, set it here + send_cmd_slow( CMD16, 512, $15 ) + ' card is mounted, make sure the CRC is turned off + send_cmd_slow( CMD59, 0, $91 ) + ' check the status + 'send_cmd_slow( CMD13, 0, $0D ) + ' done with the SPI bus for now + outa |= maskCS + ' set my counter modes for super fast SPI operation + ' writing: NCO single-ended mode, output on DI + writeMode := (%00100 << 26) | (DI << 0) + ' reading + 'readMode := (%11000 << 26) | (DO << 0) | (CLK << 9) + ' clock + 'clockLineMode := (%00110 << 26) | (CLK << 0) ' DUTY, 25% duty cycle + ' clock + clockLineMode := (%00100 << 26) | (CLK << 0) ' NCO, 50% duty cycle + ' how many bytes (8 clocks, >>3) fit into 1/2 of a second (>>1), 4 clocks per instruction (>>2)? + N_in8_500ms := clkfreq >> constant(1+2+3) + ' how long should we wait before auto-exiting any multiblock mode? + idle_limit := 125 ' ms, NEVER make this > 1000 + idle_limit := clkfreq / (1000 / idle_limit) ' convert to counts + ' Hand off control to the assembly engine's cog + bufAdr := @SPI_buffer_address + sdAdr := @SPI_block_index + SPI_command := 0 ' just make sure it's not 1 + ' start my driver cog and wait till I hear back that it's done + SPI_engine_cog := cognew( @SPI_engine_entry, @SPI_command ) + 1 + if( SPI_engine_cog == 0 ) + crash( ERR_SPI_ENGINE_NOT_RUNNING ) + repeat while SPI_command <> -1 + ' and we no longer need to control any pins from here + dira &= !maskAll + ' the return variable is card_type + +PUB release +{{ + I do not want to abort if the cog is not + running, as this is called from stop, which + is called from start/ [8^) +}} + if SPI_engine_cog + SPI_command := "z" + repeat while SPI_command == "z" + +PUB stop +{{ + kill the assembly driver cog. +}} + release + if SPI_engine_cog + cogstop( SPI_engine_cog~ - 1 ) + +PRI crash( abort_code ) +{{ + In case of Bad Things(TM) happening, + exit as gracefully as possible. +}} + ' and we no longer need to control any pins from here + dira &= !maskAll + ' and report our error + abort abort_code + +PRI send_cmd_slow( cmd, val, crc ) : reply | time_stamp +{{ + Send down a command and return the reply. + Note: slow is an understatement! + Note: this uses the assembly DAT variables for pin IDs, + which means that if you run this multiple times (say for + multiple SD cards), these values will change for each one. + But this is OK as all of these functions will be called + during the initialization only, before the PASM engine is + running. +}} + ' if this is an application specific command, handle it + if (cmd & $80) + ' ACMD is the command sequense of CMD55-CMD + cmd &= $7F + reply := send_cmd_slow( CMD55, 0, $65 ) + if (reply > 1) + return reply + ' the CS line needs to go low during this operation + outa |= maskCS + outa &= !maskCS + ' give the card a few cocks to finish whatever it was doing + read_32_slow + ' send the command byte + send_slow( cmd, 8 ) + ' send the value long + send_slow( val, 32 ) + ' send the CRC byte + send_slow( crc, 8 ) + ' is this a CMD12?, if so, stuff byte + if cmd == CMD12 + read_slow + ' read back the response (spec declares 1-8 reads max for SD, MMC is 0-8) + time_stamp := 9 + repeat + reply := read_slow + while( reply & $80 ) and ( time_stamp-- ) + ' done, and 'reply' is already pre-loaded + { + if dbg_ptr < (dbg_end-1) + byte[dbg_ptr++] := cmd + byte[dbg_ptr++] := reply + if (cmd&63) == 13 + ' get the second byte + byte[dbg_ptr++] := cmd + byte[dbg_ptr++] := read_slow + '} + +PRI send_slow( value, bits_to_send ) + value ><= bits_to_send + repeat bits_to_send + outa[pinCLK]~ + outa[pinDI] := value + value >>= 1 + outa[pinCLK]~~ + +PRI read_32_slow : r + repeat 4 + r <<= 8 + r |= read_slow + +PRI read_slow : r +{{ + Read back 8 bits from the card +}} + ' we need the DI line high so a read can occur + outa[pinDI]~~ + ' get 8 bits (remember, r is initialized to 0 by SPIN) + repeat 8 + outa[pinCLK]~ + outa[pinCLK]~~ + r += r + ina[pinDO] + ' error check + if( (cnt - SPI_block_index) > (clkfreq << 2) ) + crash( ERR_CARD_BUSY_TIMEOUT ) + +DAT +{{ + This is the assembly engine for doing fast block + reads and writes. This is *ALL* it does! +}} +ORG 0 +SPI_engine_entry + ' Counter A drives data out + mov ctra,writeMode + ' Counter B will always drive my clock line + mov ctrb,clockLineMode + ' set our output pins to match the pin mask + mov dira,maskAll + ' handshake that we now control the pins + neg user_request,#1 + wrlong user_request,par + ' start my seconds' counter here + mov last_time,cnt + +waiting_for_command + ' update my seconds counter, but also track the idle + ' time so we can to release the card after timeout. + call #handle_time + ' read the command, and make sure it's from the user (> 0) + rdlong user_request,par + cmps user_request,#0 wz,wc +if_be jmp #waiting_for_command + ' handle our card based commands + cmp user_request,#"r" wz +if_z jmp #read_ahead + cmp user_request,#"w" wz +if_z jmp #write_behind + cmp user_request,#"z" wz +if_z jmp #release_card + ' time requests are handled differently + cmp user_request,#"t" wz ' time +if_z wrlong seconds,sdAdr ' seconds goes into the SD index register +if_z wrlong dtime,bufAdr ' the remainder goes into the buffer address register + ' in all other cases, clear the user's request + mov user_request,#0 + wrlong user_request,par + jmp #waiting_for_command + + +release_card + mov user_cmd,#"z" ' request a release + neg lastIndexPlus,#1 ' reset the last block index + neg user_idx,#1 ' and make this match it + call #handle_command + mov user_request,user_cmd + wrlong user_request,par + jmp #waiting_for_command + +read_ahead + rdlong user_idx,sdAdr + ' if the correct block is not already loaded, load it + mov tmp1,user_idx + add tmp1,#1 + cmp tmp1,lastIndexPlus wz +if_z cmp lastCommand,#"r" wz +if_z jmp #:get_on_with_it + mov user_cmd,#"r" + call #handle_command +:get_on_with_it + ' copy the data up into Hub RAM + movi transfer_long,#%000010_000 'set to wrlong + call #hub_cog_transfer + ' signify that the data is ready, Spin can continue + mov user_request,user_cmd + wrlong user_request,par + ' request the next block + mov user_cmd,#"r" + add user_idx,#1 + call #handle_command + ' done + jmp #waiting_for_command + +write_behind + rdlong user_idx,sdAdr + ' copy data in from Hub RAM + movi transfer_long,#%000010_001 'set to rdlong + call #hub_cog_transfer + ' signify that we have the data, Spin can continue + mov user_request,user_cmd + wrlong user_request,par + ' write out the block + mov user_cmd,#"w" + call #handle_command + ' done + jmp #waiting_for_command + +{{ + Set user_cmd and user_idx before calling this +}} +handle_command + ' Can we stay in the old mode? (address = old_address+1) && (old mode == new_mode) + cmp lastIndexPlus,user_idx wz +if_z cmp user_cmd,lastCommand wz +if_z jmp #:execute_block_command + ' we fell through, must exit the old mode! (except if the old mode was "release") + cmp lastCommand,#"w" wz +if_z call #stop_mb_write + cmp lastCommand,#"r" wz +if_z call #stop_mb_read + ' and start up the new mode! + cmp user_cmd,#"w" wz +if_z call #start_mb_write + cmp user_cmd,#"r" wz +if_z call #start_mb_read + cmp user_cmd,#"z" wz +if_z call #release_DO +:execute_block_command + ' track the (new) last index and command + mov lastIndexPlus,user_idx + add lastIndexPlus,#1 + mov lastCommand,user_cmd + ' do the block read or write or terminate! + cmp user_cmd,#"w" wz +if_z call #write_single_block + cmp user_cmd,#"r" wz +if_z call #read_single_block + cmp user_cmd,#"z" wz +if_z mov user_cmd,#0 + ' done +handle_command_ret + ret + +{=== these PASM functions get me in and out of multiblock mode ===} +release_DO + ' we're already out of multiblock mode, so + ' deselect the card and send out some clocks + or outa,maskCS + call #in8 + call #in8 + ' if you are using pull-up resistors, and need all + ' lines tristated, then uncomment the following line. + ' for Cluso99 + 'mov dira,#0 +release_DO_ret + ret + +start_mb_read + movi block_cmd,#CMD18<<1 + call #send_SPI_command_fast +start_mb_read_ret + ret + +stop_mb_read + movi block_cmd,#CMD12<<1 + call #send_SPI_command_fast + call #busy_fast +stop_mb_read_ret + ret + +start_mb_write + movi block_cmd,#CMD25<<1 + call #send_SPI_command_fast +start_mb_write_ret + ret + +stop_mb_write + call #busy_fast + ' only some cards need these extra clocks + mov tmp1,#16 +:loopity + call #in8 + djnz tmp1,#:loopity + ' done with hack + movi phsa,#$FD<<1 + call #out8 + call #in8 ' stuff byte + call #busy_fast +stop_mb_write_ret + ret + +send_SPI_command_fast + ' make sure we have control of the output lines + mov dira,maskAll + ' make sure the CS line transitions low + or outa,maskCS + andn outa,maskCS + ' 8 clocks + call #in8 + ' send the data + mov phsa,block_cmd ' do which ever block command this is (already in the top 8 bits) + call #out8 ' write the byte + mov phsa,user_idx ' read in the desired block index + shl phsa,adrShift ' this will multiply by 512 (bytes/sector) for MMC and SD + call #out8 ' move out the 1st MSB ' + rol phsa,#1 + call #out8 ' move out the 1st MSB ' + rol phsa,#1 + call #out8 ' move out the 1st MSB ' + rol phsa,#1 + call #out8 ' move out the 1st MSB ' + ' bogus CRC value + call #in8 ' in8 looks like out8 with $FF + ' CMD12 requires a stuff byte + shr block_cmd,#24 + cmp block_cmd,#CMD12 wz +if_z call #in8 ' 8 clocks + ' get the response + mov tmp1,#9 +:cmd_response + call #in8 + test readback,#$80 wc,wz +if_c djnz tmp1,#:cmd_response +if_nz neg user_cmd,readback + ' done +send_SPI_command_fast_ret + ret + + +busy_fast + mov tmp1,N_in8_500ms +:still_busy + call #in8 + cmp readback,#$FF wz +if_nz djnz tmp1,#:still_busy +busy_fast_ret + ret + + +out8 + andn outa,maskDI + 'movi phsb,#%11_0000000 + mov phsb,#0 + movi frqb,#%01_0000000 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + mov frqb,#0 + ' don't shift out the final bit...already sent, but be aware + ' of this when sending consecutive bytes (send_cmd, for e.g.) +out8_ret + ret + +{ +in8 + or outa,maskDI + mov ctra,readMode + ' Start my clock + mov frqa,#1<<7 + mov phsa,#0 + movi phsb,#%11_0000000 + movi frqb,#%01_0000000 + ' keep reading in my value, one bit at a time! (Kuneko - "Wh) + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + mov frqb,#0 ' stop the clock + mov readback,phsa + mov frqa,#0 + mov ctra,writeMode +in8_ret + ret +} +in8 + neg phsa,#1' DI high + mov readback,#0 + ' set up my clock, and start it + movi phsb,#%011_000000 + movi frqb,#%001_000000 + ' keep reading in my value + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + mov frqb,#0 ' stop the clock + rcl readback,#1 + mov phsa,#0 'DI low +in8_ret + ret + + +' this is called more frequently than 1 Hz, and +' is only called when the user command is 0. +handle_time + mov tmp1,cnt ' get the current timestamp + add idle_time,tmp1 ' add the current time to my idle time counter + sub idle_time,last_time ' subtract the last time from my idle counter (hence delta) + add dtime,tmp1 ' add to my accumulator, + sub dtime,last_time ' and subtract the old (adding delta) + mov last_time,tmp1 ' update my "last timestamp" + rdlong tmp1,#0 ' what is the clock frequency? + cmpsub dtime,tmp1 wc ' if I have more than a second in my accumulator + addx seconds,#0 ' then add it to "seconds" + ' this part is to auto-release the card after a timeout + cmp idle_time,idle_limit wz,wc +if_b jmp #handle_time_ret ' don't clear if we haven't hit the limit + mov user_cmd,#"z" ' we can't overdo it, the command handler makes sure + neg lastIndexPlus,#1 ' reset the last block index + neg user_idx,#1 ' and make this match it + call #handle_command ' release the card, but don't mess with the user's request register +handle_time_ret + ret + +hub_cog_transfer +' setup for all 4 passes + mov ctrb,clockXferMode + mov frqb,#1 + rdlong buf_ptr,bufAdr + mov ops_left,#4 + movd transfer_long,#speed_buf +four_transfer_passes + ' sync to the Hub RAM access + rdlong tmp1,tmp1 + ' how many long to move on this pass? (512 bytes / 4)longs / 4 passes + mov tmp1,#(512 / 4 / 4) + ' get my starting address right (phsb is incremented 1 per clock, so 16 each Hub access) + mov phsb,buf_ptr + ' write the longs, stride 4...low 2 bits of phsb are ignored +transfer_long + rdlong 0-0,phsb + add transfer_long,incDest4 + djnz tmp1,#transfer_long + ' go back to where I started, but advanced 1 long + sub transfer_long,decDestNminus1 + ' offset my Hub pointer by one long per pass + add buf_ptr,#4 + ' do all 4 passes + djnz ops_left,#four_transfer_passes + ' restore the counter mode + mov frqb,#0 + mov phsb,#0 + mov ctrb,clockLineMode +hub_cog_transfer_ret + ret + + +read_single_block + ' where am I sending the data? + movd :store_read_long,#speed_buf + mov ops_left,#128 + ' wait until the card is ready + mov tmp1,N_in8_500ms +:get_resp + call #in8 + cmp readback,#$FE wz +if_nz djnz tmp1,#:get_resp +if_nz neg user_cmd,#ERR_ASM_NO_READ_TOKEN +if_nz jmp #read_single_block_ret + ' set DI high + neg phsa,#1 + ' read the data + mov ops_left,#128 +:read_loop + mov tmp1,#4 + movi phsb,#%011_000000 +:in_byte + ' Start my clock + movi frqb,#%001_000000 + ' keep reading in my value, BACKWARDS! (Brilliant idea by Tom Rokicki!) + test maskDO,ina wc + rcl readback,#8 + test maskDO,ina wc + muxc readback,#2 + test maskDO,ina wc + muxc readback,#4 + test maskDO,ina wc + muxc readback,#8 + test maskDO,ina wc + muxc readback,#16 + test maskDO,ina wc + muxc readback,#32 + test maskDO,ina wc + muxc readback,#64 + test maskDO,ina wc + mov frqb,#0 ' stop the clock + muxc readback,#128 + ' go back for more + djnz tmp1,#:in_byte + ' make it...NOT backwards [8^) + rev readback,#0 +:store_read_long + mov 0-0,readback ' due to some counter weirdness, we need this mov + add :store_read_long,const512 + djnz ops_left,#:read_loop + + ' set DI low + mov phsa,#0 + + ' now read 2 trailing bytes (CRC) + call #in8 ' out8 is 2x faster than in8 + call #in8 ' and I'm not using the CRC anyway + ' give an extra 8 clocks in case we pause for a long time + call #in8 ' in8 looks like out8($FF) + + ' all done successfully + mov idle_time,#0 + mov user_cmd,#0 +read_single_block_ret + ret + +write_single_block + ' where am I getting the data? (all 512 bytes / 128 longs of it?) + movs :write_loop,#speed_buf + ' read in 512 bytes (128 longs) from Hub RAM and write it to the card + mov ops_left,#128 + ' just hold your horses + call #busy_fast + ' $FC for multiblock, $FE for single block + movi phsa,#$FC<<1 + call #out8 + mov phsb,#0 ' make sure my clock accumulator is right + 'movi phsb,#%11_0000000 +:write_loop + ' read 4 bytes + mov phsa,speed_buf + add :write_loop,#1 + ' a long in LE order is DCBA + rol phsa,#24 ' move A7 into position, so I can do the swizzled version + movi frqb,#%010000000 ' start the clock (remember A7 is already in place) + rol phsa,#1 ' A7 is going out, at the end of this instr, A6 is in place + rol phsa,#1 ' A5 + rol phsa,#1 ' A4 + rol phsa,#1 ' A3 + rol phsa,#1 ' A2 + rol phsa,#1 ' A1 + rol phsa,#1 ' A0 + rol phsa,#17 ' B7 + rol phsa,#1 ' B6 + rol phsa,#1 ' B5 + rol phsa,#1 ' B4 + rol phsa,#1 ' B3 + rol phsa,#1 ' B2 + rol phsa,#1 ' B1 + rol phsa,#1 ' B0 + rol phsa,#17 ' C7 + rol phsa,#1 ' C6 + rol phsa,#1 ' C5 + rol phsa,#1 ' C4 + rol phsa,#1 ' C3 + rol phsa,#1 ' C2 + rol phsa,#1 ' C1 + rol phsa,#1 ' C0 + rol phsa,#17 ' D7 + rol phsa,#1 ' D6 + rol phsa,#1 ' D5 + rol phsa,#1 ' D4 + rol phsa,#1 ' D3 + rol phsa,#1 ' D2 + rol phsa,#1 ' D1 + rol phsa,#1 ' D0 will be in place _after_ this instruction + mov frqb,#0 ' shuts the clock off, _after_ this instruction + djnz ops_left,#:write_loop + ' write out my two (bogus, using $FF) CRC bytes + call #in8 + call #in8 + ' now read response (I need this response, so can't spoof using out8) + call #in8 + and readback,#$1F + cmp readback,#5 wz +if_z mov user_cmd,#0 ' great +if_nz neg user_cmd,#ERR_ASM_BLOCK_NOT_WRITTEN ' oops + ' send out another 8 clocks + call #in8 + ' all done + mov idle_time,#0 +write_single_block_ret + ret + + +{=== Assembly Interface Variables ===} +pinDO long 0 ' pin is controlled by a counter +pinCLK long 0 ' pin is controlled by a counter +pinDI long 0 ' pin is controlled by a counter +maskDO long 0 ' mask for reading the DO line from the card +maskDI long 0 ' mask for setting the pin high while reading +maskCS long 0 ' mask = (1<Syntax for zx is:
- zx comfile.com arg1 arg2 ... + zx comfile.com [-stdio] arg1 arg2 ...

The comfile is the program to run; zx searches the current @@ -153,6 +153,10 @@ directory and BINDIR80 for it.

The arguments are parsed in this way:

    +
  • If -stdio appears as one of the arguments, zx will use buffered DOS I/O + which is useful if you want to pipe data in or out of zx. Otherwise + zx will use raw console I/O which works better when the application + is being used interactively.
  • Any argument starting with a - sign is passed to the CP/M program as-is, minus the leading - sign.
  • Any argument starting with a + sign is parsed as a filename (see below) diff --git a/Tools/zx/zxdbg.exe b/Tools/zx/zxdbg.exe new file mode 100644 index 00000000..5f13ad59 Binary files /dev/null and b/Tools/zx/zxdbg.exe differ