{ --------------------------------------------------------------------- { { WISP.PAS V4.13 dd 23-Feb-2004 { { Tool to control { WISP programmer hardware (obsolete) { Wisp628 programmer hardware (but for a limited range of targets) { WLoader 16f877 application loader { { see: http://www.voti.nl/wisptool { { Note: XWisp is the recommended PC software for Wisp628 and WLoader. { { --------------------------------------------------------------------- { { Copyright (C) 2000 .. 2004 Wouter van Ooijen { { This application is free software; you can redistribute it and/or { modify it under the terms of the GNU Library General Public { License as published by the Free Software Foundation; either { version 2 of the License, or (at your option) any later version. { { This application is distributed in the hope that it will be useful, { but WITHOUT ANY WARRANTY; without even the implied warranty of { MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU { Library General Public License for more details. { { You should have received a copy of the GNU Library General Public { License along with this library; if not, write to the { Free Software Foundation, Inc., 59 Temple Place - Suite 330, { Boston, MA 02111-1307, USA. { { --------------------------------------------------------------------- } { { ToDo list { - verification with code protection { - WLoader fuses discrepancy: just a warning { - TTY show mode, add example, does not work? { - put/get etc. picture { { --------------------------------------------------------------------- } uses dos, crt; const version = '4.13'; function min( a, b : integer ) : integer; begin if a > b then min := b else min := a; end; const ModeTextIndex : integer = 1; const ModeText : array[ 1..4] of string = ( 'B67T', 'B67I', 'AUXT', 'AUXI' ); { --------------------------------------------------------------------- } { { string things { { --------------------------------------------------------------------- } const HexArray : string = '0123456789ABCDEF'; { return decimal string of n } function DecInt( n : integer ) : string; var s : string; begin Str( n, s ); DecInt := s; end; { return one hex char from a 4 bits nibble } function HexNibble( b : byte ) : char; begin HexNibble := HexArray[ 1 + ( b mod $10 ) ]; end; { return two hex chars from an 8 bits byte } function HexByte( b : byte ) : string; begin HexByte := HexNibble( b div $10 ) + HexNibble( b mod $10 ); end; { return two hex chars from a (8 bit) char } function HexChar( c : char ) : string; begin HexChar := HexByte( ord( c ) ); end; { return four hex chars from a 16 bit word } function HexWord( w : word ) : string; begin HexWord := HexByte( hi( w ) ) + HexByte( lo( w ) ); end; { return 8 bit chars from an 8 bit byte } function BinByte( b : byte ) : string; var s : string; var i : integer; begin s := ''; for i:= 1 to 8 do begin if odd( b ) then s := '1' + s else s := '0' + s; b := b div 2; end; BinByte := s; end; { return a 4-bit nibble from one hex char } function NibbleHex( c : char ) : byte; begin case c of '0'..'9': NibbleHex := ord( c ) - ord( '0' ); 'A'..'F': NibbleHex := ord( c ) - ord( 'A' ) + 10; 'a'..'f': NibbleHex := ord( c ) - ord( 'a' ) + 10; else Nibblehex := 0; end; end; { return a 8-bit byte from two hex chars } function ByteHex( s : string ) : byte; begin ByteHex := $10 * Nibblehex( s[ 1 ] ) + NibbleHex( s[ 2 ] ); end; { return a 16-bits word from four hex chars } function WordHex( s : string ) : word; begin WordHex := $1000 * NibbleHex( s[ 1 ] ) + $100 * NibbleHex( s[ 2 ] ) + $10 * NibbleHex( s[ 3 ] ) + NibbleHex( s[ 4 ] ) end; { return substring of s starting at n, i long } function Substr( s : string; n, i : integer ) : string; begin if ( i = 0 ) or ( n > length( s ) ) then substr := '' else substr := s[ n ] + substr( s, n + 1, i - 1 ); end; function LowCase( c : char ) : char; begin if ( c >= 'A' ) and ( c <= 'Z' ) then begin LowCase := chr( ord( c ) + ord( 'a' ) - ord( 'A' ) ); end else begin LowCase := c; end; end; { return uppercase version of s } function UpStr( s : string ) : string; var i : integer; begin for i:=1 to length( s ) do begin s[i] := upcase( s[ i ] ); end; UpStr := s; end; { return string version of integer x with leading zero's to length n } function IntStr( x : longint; n : integer ) : string; var s : string; begin str( x, s ); while length(s) < n do begin s := '0' + s; end; IntStr := s; end; procedure Pivot( x : string; c : char; var s1 : string; var s2 : string ); var i : integer; var found : boolean; begin s1 := ''; s2 := ''; found := false; for i:=1 to length( x ) do begin if x[ i ] = c then begin found := true; end else begin if found then begin s2 := s2 + x[ i ]; end else begin s1 := s1 + x[ i ]; end; end; end; end; { --------------------------------------------------------------------- } { { timing { { --------------------------------------------------------------------- } var xx : longint; { return a monotonous time in milliseconds, { resolution according to BIOS timer interrupt. } function TurboMilliSeconds : longint; var h,m,s,s100 : word; function f(x:word):longint;begin f:=x; end; var x : longint; begin GetTime( h, m, s, s100 ); { x := 60 * 1000 * f(m) + 1000 * f(s) + 10 * f(s100); if x = xx then write('.') else begin writeln('tm=',x); xx := x; end; } TurboMilliSeconds := 60 * 1000 * f(m) + 1000 * f(s) + 10 * f(s100); end; { return the number of clock ticks since power up } function BiosTicks : longint; var l : word absolute $40:$6c; var h : word absolute $40:$6e; begin write ( '{', l, ':', h, '}' ); BiosTicks := longint(l) + longint(65536) * longint(h); end; { return the PIT down counter value } procedure short_delay; inline( $EB/$00/$EB/$00 ); function PitValue : longint; var c1, c2 : longint; begin port[$43] := $00; { latch counter 0 contents } short_delay; c1 := port[$40]; { read low byte } short_delay; c2 := port[$40]; { read high byte } writeln( '[ ', c1, ' ', c2, ' ',c1 + 256 * longint(c2), ' ]' ); PitValue := c1 + 256 * longint(c2); end; { return the number of milliseconds since start up } const BiosStart : longint = 0; function Milliseconds : longint; var b : longint; var p : word; var Done : boolean; begin Milliseconds := TurboMilliseconds; { if BiosStart = 0 then BiosStart := BiosTicks; repeat b := BiosTicks; p := PitValue; Milliseconds := (((b - BiosStart + 1) * 65536 ) - p ) div 1193; until b = BiosTicks; } end; { return string version of current time } function TimeStr : string; var h,m,s,s100 : word; begin GetTime( h, m, s, s100 ); TimeStr := IntStr( h, 2 ) + ':' + IntStr( m, 2 ) + ':' + IntStr( s, 2 ) + '.' + IntStr( s100, 2 ) + ' (' + IntStr( TurboMilliseconds, 8 ) + ')'; end; var TimeoutTime : longint; { start timeout of n milliseconds } procedure StartTimeout( n : longint ) ; begin TimeoutTime := Milliseconds + n; end; { return true iff started timeout has expired } function Timeout : boolean; begin Timeout := Milliseconds >= TimeoutTime; end; { delay for n milliseconds } procedure Delay( n : longint ); begin StartTimeout( n ); while not Timeout do ; end; { --------------------------------------------------------------------- } { { error handling and logging { { --------------------------------------------------------------------- } { global flag's etc. } var LogFile : text; const LogOpen : boolean = false; const FlushEnable : boolean = false; const BeepEnable : boolean = false; const VerboseEnable : boolean = false; const DumpInterval : integer = 16; { make a sound { f = start frequency, fd = frequency delta, { d1 = sound duration, d2 = pause duration { n = number of sound/pause cycles } procedure Beep( f, fd, d1, d2, n : integer ); var i : integer; begin for i:=1 to n do begin sound( f ); delay( d1 ); nosound; delay( d2 ); f := f + fd; end; end; { make an OK (rising) sound } procedure BeepOK; begin if BeepEnable then begin beep( 1000, 100, 80, 20, 7 ); end; end; { make an Error (lowering) sound } procedure BeepError; begin if BeepEnable then begin beep( 1000, -100, 80, 20, 7 ); end; end; { start logging to the indicated file } procedure LogOpenFile( s : string ); begin if LogOpen then begin close( LogFile ); end; assign( LogFile, s ); rewrite( LogFile ); LogOpen := true; end; { log to the log file - if one is open - and { to the screen - if screen logging is enabled } procedure Log( s : string ); begin if LogOpen then begin writeln( LogFile, TimeStr + ' ' + s ); if FlushEnable then flush( LogFile ); end; if VerboseEnable then begin writeln( TimeStr + ' ' + s ); end; end; { log to the screen and to the log file - { if one is open. } procedure Show( s : string ); begin Log( s ); if not VerboseEnable then begin writeln( TimeStr + ' ' + s ); end; end; procedure Set_DTR( r : boolean ); forward; procedure WaitIgnoreSendString( s : string ); forward; { report a fatal error and quit } const Quitting : boolean = false; procedure Fatal( s : string ); begin if not Quitting then begin Quitting := true; writeln; Show( 'fatal: ' + s ); if LogOpen then begin close( LogFile ); end; BeepError; Set_DTR( false ); WaitIgnoreSendString( '0000G' ); end; halt; end; { --------------------------------------------------------------------- } { { IO Port handling { { --------------------------------------------------------------------- } const TxDataReg = 0; { transmitter data register } RxDataReg = 0; { reciever data register } DivLow = 0; { divisor latch, low byte } DivHigh = 1; { divisor latch, high byte } IntrEnable = 1; { interrupt enable register } IntrId = 2; { interrupt identification register } FifoCtrl = 2; { first-in/first-out buffer controller } LineCtrl = 3; { line controll register } ModemCtrl = 4; { modem controll register } LineStatus = 5; { line status register } ModemStatus = 6; { modem status register } ScratchReg = 7; { scratch pad (free useable) } var PortBase : word; var Ports : array[ 1 .. 8 ] of word absolute $40 : $00; { init using port nr p with baudrate b } procedure InitPort( p : integer; b : longint ); var d : word; begin PortBase := Ports[ p ]; { PortBase := $3F8; } Log( 'init port ' + HexByte( p ) + ' at ' + HexWord( PortBase ) + ' baudrate ' + IntStr( b, 0 ) + ' start' ); { Calculate the divisor latch contents for the desired baud rate } D := (115200 div b); { Set the DLAB bit to 1, then write the divisor latch low and high bytes } Port[ PortBase + LineCtrl] := $80; Port[ PortBase + DivLow] := Lo(D); Port[ PortBase + DivHigh] := Hi(D); { Now set the divisor latch bit to 0 and write the other values } Port[ PortBase + LineCtrl] := $03; { Port[ PortBase + ModemCtrl] := $0A; } Port[ PortBase + IntrEnable] := $00; Log( 'init port end' ); end; procedure set_DTR( r : boolean );begin if r then begin Port[ PortBase + ModemCtrl] := Port[ PortBase + ModemCtrl] or 1; end else begin Port[ PortBase + ModemCtrl] := Port[ PortBase + ModemCtrl] and ( not 1 ); end; end; procedure set_RTS( r : boolean );begin if r then begin Port[ PortBase + ModemCtrl] := Port[ PortBase + ModemCtrl] or 2; end else begin Port[ PortBase + ModemCtrl] := Port[ PortBase + ModemCtrl] and ( not 2 ); end; end; const DTR_specified : boolean = false; procedure ResetTarget; begin if not DTR_specified then begin set_DTR( true ); delay( 100 ); set_DTR( false ); delay( 300 ); { was 100 } end; end; const tty_emulation : boolean = false; const echo_device : boolean = false; { check line status, fatal if any error } procedure CheckLineStatus; var s : word; begin s := port[ PortBase + LineStatus ] and $0E; { enabling this check gives lots of framing errors, why? } { if not tty_emulation then begin if s <> 0 then Fatal( ' LineStatus : ' + BinByte( s ) ); end;} end; { ignore a possible line status error } procedure IgnoreLineStatus; var d : word; begin d := port[ PortBase + LineStatus ]; end; { return whether a received char is waiting } function CharReady : boolean; begin CheckLineStatus; CharReady := port[ PortBase + LineStatus ] and $01 <> $00; end; { return whether a char can be sent } function SendReady : boolean; begin SendReady := port[ PortBase + LineStatus ] and $60 = $60; { SendReady := port[ PortBase + LineStatus ] and $20 = $20; } end; { get received char } procedure GetChar( var c : char ); begin c := chr( port[ PortBase ] ); if true then begin if c >' ' then Log( 'receive ['+c+'] ' + HexByte( ord( c ) ) ) else Log( 'receive hex [' + HexByte( ord( c ) ) + ']' ); end; end; { send a char } procedure SendChar( c : char ); begin if echo_device then begin Log( 'send ['+c+'] high bit set' ); port[ PortBase ] := ord( c ) + 128; end else begin Log( 'send ['+c+'] ' + HexChar( c ) ); port[ PortBase ] := ord( c ); end; end; { clear rceive status } procedure ClearReceive; var c : char; var d : word; begin Log( 'clear receive' ); Delay( 100 ); IgnoreLineStatus; while CharReady do begin while CharReady do GetChar( c ); Delay( 100 ); end; Log( 'clear receive end' ); CheckLineStatus; end; { clear rceive status } procedure FastClearReceive; var c : char; var d : word; begin Log( 'fast clear receive' ); IgnoreLineStatus; while CharReady do begin while CharReady do GetChar( c ); Delay( 100 ); end; Log( 'clear receive end' ); CheckLineStatus; end; { wait for tranceiver empty and send a char } procedure WaitSendChar( c : char ); begin StartTimeout( 100 ); while not SendReady do begin CheckLineStatus; if Timeout then fatal( 'communication timeout on send' ); end; SendChar( c ); end; { send a string, wait for ready for each char } procedure WaitSendString( s : string ); var i : integer; begin CheckLineStatus; for i:= 1 to length( s ) do begin WaitSendChar( s[ i ] ); end; end; { send a string, ignore incoming chars & line status } procedure WaitIgnoreSendString( s : string ); var i : integer; begin for i:= 1 to length( s ) do begin StartTimeout( 100 ); while not SendReady do begin if Timeout then fatal( 'communication timeout on send' ); end; SendChar( s[ i ] ); Delay( 100 ); end; IgnoreLineStatus; end; { check line status and receive char } procedure ReceiveChar( var c : char ); begin CheckLineStatus; GetChar( c ); end; { wait for char ready and receive char } procedure WaitReceive( var c : char ); begin Log( 'wait for char' ); StartTimeout( 1000 ); while not CharReady do begin if Timeout then begin fatal( 'communication timeout on receive' + ' : check the power and the ' + ' serial line to the WISP hardware and retry' ); end; end; ReceiveChar( c ); if ord( c ) > 128 then begin while not CharReady do begin if Timeout then begin fatal( 'communication timeout on receive' + ' : check the power and the ' + ' serial line to the WISP hardware and retry' ); end; end; ReceiveChar( c ); end; end; { send char, check echo } procedure SendReceiveChar( cs, cr : char ); var r : char; begin log( 'start waitsendchar' ); FastClearReceive; WaitSendChar( cs ); log('sending done'); WaitReceive( r ); log('receiving done'); if r <> UpCase( cr ) then begin if LowCase( r ) = cr then begin { receive again for self-echoing hardware } echo_device := true; WaitReceive( r ) end; if r <> UpCase( cr ) then begin fatal( 'send [' + cs + '] ' + BinByte( ord( cs ) ) + ' expected [' + cr + '] ' + BinByte( ord( cr ) ) + ' received [' + r + '] ' + BinByte( ord( r ) ) + ' : check the connection to the target and retry ' ); end; end; end; { send string, check echo } procedure SendReceive( s : string ); var i : integer; begin for i:=1 to length(s) do begin SendReceiveChar( s[ i ], s[ i ] ); end; end; { get response buffer (response to to x * 'n'), } { or up to a ' ' when the first char is a ' ' } procedure GetLongResponse( x : integer; var s : string ); var c : char; var i : integer; begin s := ''; FastClearReceive; WaitSendChar( 'n' ); WaitReceive( c ); if c = 'n' then begin WaitReceive( c ); end; if c = ' ' then begin s := ''; c := 'x'; while ( c <> ' ' ) and ( length( s ) < 32 ) do begin WaitSendChar( 'n' ); WaitReceive( c ); if c = 'n' then begin WaitReceive( c ); end; if c <> ' ' then s := s + c; end; end else begin s := c; for i := 2 to x do begin log( 'send ' ); WaitSendChar( 'n' ); log('done '); { ignorelinestatus; } WaitReceive( c ); log('done '); if c = 'n' then begin WaitReceive( c ); end; s := s + c; end; end; end; { get response buffer (response to to x * 'n') } procedure GetResponse( x : integer; var s : string ); var c : char; var i : integer; begin s := ''; FastClearReceive; for i := 1 to x do begin log( 'send ' ); WaitSendChar( 'n' ); log('done '); { ignorelinestatus; } WaitReceive( c ); log('done '); if c = 'n' then begin WaitReceive( c ); end; s := s + c; end; end; { get response from SX-Key } procedure GetSX( q : string; x : byte; var s : string ); var c : char; var i : byte; begin s := ''; {Fast} ClearReceive; StartTimeout( 1000 ); for i:= 1 to length( q ) do begin if CharReady then begin ReceiveChar( c ); s := s + c; x := x - 1; end; while not SendReady do begin if CharReady then begin ReceiveChar( c ); s := s + c; x := x - 1; end; CheckLineStatus; if Timeout then begin i := port[ PortBase + LineStatus ]; fatal( 'communication timeout on send, status=' + HexByte( i ) + ' ' + BinByte( i ) ); end; end; SendChar( q[ i ] ); end; for i := 1 to x do begin while not CharReady do begin if Timeout then begin fatal( 'communication timeout on receive' + '(n=' + IntStr( x + 1 - i, 0 ) + ')' + ' : check the power and the ' + ' serial line to the SX-Key and retry' ); end; end; ReceiveChar( c ); s := s + c; end; end; { issue a line break } procedure LineBreak; begin Log( 'line break start' ); Port[ PortBase + LineCtrl] := $43; delay( 200 ); Port[ PortBase + LineCtrl] := $03; delay( 100 ); Log( 'line break end' ); end; const show_hex : boolean = false; { emulate TTY on port p with baudrate b } procedure EmulateTTY( p : word; m : string; b : longint ); var cx, cr, c2 : char; var s : string; begin tty_emulation := true; InitPort( p, b ); cr := ' '; if b > 1000 then begin s := IntStr( b div 1000, 0 ) + 'k' + IntStr( b mod 1000, 0 ); end else begin s := IntStr( b, 0 ); end ; write( 'TTY mode ' + s + ' baud, ' ); if m <> '' then write( m + ', ' ); writeln( 'quit with ' ); repeat IgnoreLineStatus; if CharReady then begin GetChar( cx ); { writeln( 'x=' + HexByte( byte( cx ) ) ); } write( cx ); if show_hex then write( '[' + HexChar( cx ) + ']' ); end else if keypressed then begin c2 := cr; cr := readkey; WaitSendChar( cr ); end; until (cr = chr(27)) and (c2 = chr(27)); writeln; writeln( 'TTY mode end' ); tty_emulation := false; end; { --------------------------------------------------------------------- } { { target handling { { --------------------------------------------------------------------- } type target_type = ( pic_unknown, pic_12f629, pic_12f675, pic_16f630, pic_16f676, pic_16c84, pic_16f84, pic_16f84a, pic_16f627, pic_16f628, pic_16f73, pic_16f74, pic_16f76, pic_16f77, pic_16f870, pic_16f871, pic_16f872, pic_16f873, pic_16f874, pic_16f876, pic_16f877 ); const target : target_type = pic_unknown; const target_first = pic_unknown; const target_last = pic_16f877; procedure Must_Know_Target; begin if target = pic_unknown then begin fatal( 'target must be specified' ); end; end; type algorithm_type = ( Algorithm_PIC16, Algorithm_PIC16A, Algorithm_PIC16C ); const Algorithms : array[ algorithm_type ] of record Code : string; Data : string; Fuses : string; ID : string; Erase : string; end = ( ( Code : '000cx' ; Data : '000dx' ; Fuses : '000fx' ; ID : '000fx' ; Erase : '000ex' ), ( Code : '001cx' ; Data : '001dx' ; Fuses : '001fx' ; ID : '001fx' ; Erase : '001cex' ), ( Code : '004cx' ; Data : '004dx' ; Fuses : '004fx' ; ID : '004fx' ; Erase : '004ex' ) ); type preserve_type = ( Preserve_Nothing, Preserve_1K ); const TargetInfo : array[ target_type ] of record Name : string; Abbreviation : string; RomSize : integer; DataSize : integer; ProtectionMask : integer; Id : integer; IdMask : integer; ReadFuses : boolean; Preserve : preserve_type; Fixed_One : integer; Fixed_Zero : integer; Algorithm : algorithm_type; end = ( ( Name : ''; Abbreviation : ''; RomSize : 0; DataSize : 0; ProtectionMask : 0; Id : 0; IdMask : 0; ReadFuses : false; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '12f629'; Abbreviation : 'f629'; RomSize : 1024; DataSize : 64; ProtectionMask : $3E7F; Id : $0F80; IdMask : $001f; ReadFuses : true; Preserve : Preserve_1K; Fixed_One : 0; Fixed_Zero : $0E00; Algorithm : Algorithm_PIC16C ), ( Name : '12f675'; Abbreviation : 'f675'; RomSize : 1024; DataSize : 64; ProtectionMask : $3E7F; Id : $0FC0; IdMask : $001f; ReadFuses : true; Preserve : Preserve_1K; Fixed_One : 0; Fixed_Zero : $0E00; Algorithm : Algorithm_PIC16C ), ( Name : '16f630'; Abbreviation : 'f630'; RomSize : 1024; DataSize : 64; ProtectionMask : $3E7F; Id : $10C0; IdMask : $001f; ReadFuses : true; Preserve : Preserve_1K; Fixed_One : 0; Fixed_Zero : $0E00; Algorithm : Algorithm_PIC16C ), ( Name : '16f676'; Abbreviation : 'f676'; RomSize : 1024; DataSize : 64; ProtectionMask : $3E7F; Id : $10E0; IdMask : $001f; ReadFuses : true; Preserve : Preserve_1K; Fixed_One : 0; Fixed_Zero : $0E00; Algorithm : Algorithm_PIC16C ), ( Name : '16c84'; Abbreviation : 'c84'; RomSize : 1024; DataSize : 64; ProtectionMask : $000f; Id : $0000; IdMask : 0; ReadFuses : false; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f84'; Abbreviation : 'f84'; RomSize : 1024; DataSize : 64; ProtectionMask : $000f; Id : $0000; IdMask : 0; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f84a'; Abbreviation : 'f84a'; RomSize : 1024; DataSize : 64; ProtectionMask : $000f; Id : $0560; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f627'; Abbreviation : 'f627'; RomSize : 1024; DataSize : 128; ProtectionMask : $02ff; Id : $07e0; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f628'; Abbreviation : 'f628'; RomSize : 2048; DataSize : 128; ProtectionMask : $02ff; Id : $07c0; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f73'; Abbreviation : 'f73'; RomSize : 4096; DataSize : 128; ProtectionMask : $3fef; Id : $0600; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16A ), ( Name : '16f74'; Abbreviation : 'f74'; RomSize : 4096; DataSize : 128; ProtectionMask : $3fef; Id : $0620; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16A ), ( Name : '16f76'; Abbreviation : 'f76'; RomSize : 8192; DataSize : 256; ProtectionMask : $3fef; Id : $0640; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16A ), ( Name : '16f77'; Abbreviation : 'f77'; RomSize : 8192; DataSize : 256; ProtectionMask : $3fef; Id : $0660; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16A ), ( Name : '16f870'; Abbreviation : 'f870'; RomSize : 2048; DataSize : 64; ProtectionMask : $0ecf; Id : $0d00; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f871'; Abbreviation : 'f871'; RomSize : 2048; DataSize : 64; ProtectionMask : $0cdf; Id : $0d20; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f872'; Abbreviation : 'f872'; RomSize : 2048; DataSize : 64; ProtectionMask : $0cdf; Id : $08e0; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f873'; Abbreviation : 'f873'; RomSize : 4096; DataSize : 128; ProtectionMask : $0cdf; Id : $0960; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f874'; Abbreviation : 'f874'; RomSize : 4096; DataSize : 128; ProtectionMask : $0cdf; Id : $0920; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f876'; Abbreviation : 'f876'; RomSize : 8192; DataSize : 256; ProtectionMask : $0cdf; Id : $09e0; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ), ( Name : '16f877'; Abbreviation : 'f877'; RomSize : 8192; DataSize : 256; ProtectionMask : $0cdf; Id : $09a0; IdMask : $001f; ReadFuses : true; Preserve : Preserve_Nothing; Fixed_One : 0; Fixed_Zero : 0; Algorithm : Algorithm_PIC16 ) ); { --------------------------------------------------------------------- } { { program image handling { { --------------------------------------------------------------------- } const Pic_Image_Last = $4fff; const Dummy_Value = $ffff; const Config_Address = $2007; type Pic_Image = record data : array[ 0 .. Pic_Image_Last ] of word; end; type Image_Part = ( code, data, code_1k, fuses ); type Image_Part_Set = set of Image_Part; const All_Parts : Image_Part_Set = [ code, data, fuses ]; const Preserved_Regions_1K : Image_Part_Set = [ code_1k, fuses ]; const Image_Info : array[ Image_Part ] of record name : string; start : word; first : word; last : word; mask : word; end = ( ( name: 'code'; start: $0000; first: $0000; last: $1fff; mask: $3fff ), ( name: 'data'; start: $2100; first: $2100; last: $21ff; mask: $00ff ), ( name: 'code_1k'; start: $0000; first: $03FF; last: $03FF; mask: $3fff ), ( name: 'fuses'; start: $2000; first: $2007; last: $2007; mask: $3fff ) ); function Start_Command( Algorithm : Algorithm_Type; Part : Image_Part ) : string; begin Start_Command := ''; if ( Part = code ) or ( Part = code_1k ) then begin Start_Command := Algorithms[ Algorithm ].Code; end; if Part = data then begin Start_Command := Algorithms[ Algorithm ].Data; end; if Part = fuses then begin Start_Command := Algorithms[ Algorithm ].Fuses; end; end; function Erase_Command( Algorithm : Algorithm_Type ) : string; begin Erase_Command := Algorithms[ Algorithm ].Erase; end; procedure Add_Or_Remove( var Parts : Image_Part_Set; sign : integer; Delta : Image_Part_set ); begin if sign > 0 then begin Parts := Parts + Delta; end else begin Parts := Parts - Delta; end; end; function Image_Get( var m : Pic_Image; address : word ): word; begin Image_Get := m.data[ address ]; end; procedure Image_Set( var m : Pic_Image; address : integer; d : word );begin if ( address < 0 ) or ( address > Pic_Image_Last ) then begin fatal( 'image address out of range' ); end; m.data[ address ] := d; end; function Image_Last( p : Image_Part ) : word; var n : integer; begin if p = code then begin n := TargetInfo[ target ].RomSize; end else if p = data then begin n := $2100 + TargetInfo[ target ].DataSize; end else begin n := Pic_Image_Last; end; Image_Last := min( n, Image_Info[ p ].last ); end; { set image to all x, x+y x+2y etc. } procedure Image_Set_All( var m : Pic_Image; x, y : word ); var i : integer; begin for i := $0000 to Pic_Image_Last do begin Image_Set( m, i, x ); x := x + y; end; end; { set image to all dummy } procedure Image_Clear( var m : Pic_Image ); begin Image_Set_All( m, Dummy_Value, 0 ); end; { accumulate a byte b to checksum s } procedure accu1( var s : word; b : byte ); begin s := ( s + b ) mod $100; end; { accumulate the two bytes from word w to checksum s } procedure accu2( var s : word; w : word ); begin accu1( s, lo( w ) ); accu1( s, hi( w ) ); end; { read one hex byte (two char) from f, accumulate checksum s } function ReadHexByte( var f : text; var s : word ) : byte; var c1, c2 : char; begin read( f, c1 ); read( f, c2 ); accu1( s, ByteHex( c1 + c2 ) ); ReadHexByte := ByteHex( c1 + c2 ); end; { read one hex word (four char) from f, accumulate checksum s } function ReadHexWord( var f : text; var s : word ) : word; var b : byte; begin b := ReadHexByte( f, s ); ReadHexWord := b + $100 * ReadHexByte( f, s ); end; type Patch_List = ^Patch_Record; Patch_Record = record Address : integer; Value : word; Next : Patch_List; end; procedure Patch_List_Add( var List : Patch_List; Address : integer; Value : word ); var p : Patch_List; begin new( p ); p^.Address := Address; p^.Value := Value; p^.Next := List; List := p; end; procedure Patch_List_Apply( var Image : Pic_Image; List : Patch_List ); begin { writeln( 'patch' ); } while List <> nil do begin { writeln( List^.Address, '=', List^.Value ); } Image_Set( Image, List^.Address, List^.Value ); List := List^.Next; end; { writeln( 'end patch' ); } end; { handling of fuses word(s) } const Include_Fuses : boolean = true; const Fixed_Fuses : boolean = false; const Fuses_Value : word = 0; Procedure Patch_Fuses( var m : Pic_Image ); begin if Fixed_Fuses then begin Image_Set( m, Config_Address, Fuses_Value ); end; if not Include_Fuses then begin Image_Set( m, Config_Address, Dummy_Value ); end; end; { handling of the protection bits } type Protection_Type = ( always, hex, never ); var Protection : Protection_Type; procedure Patch_Protection( var m : Pic_Image; var p : Protection_Type ); var d : word; begin d := Image_Get( m, Config_Address ); if Include_Fuses then begin if p = always then begin Must_Know_Target; d := d and TargetInfo[ target ].ProtectionMask; end; if p = never then begin Must_Know_Target; d := d and TargetInfo[ target ].ProtectionMask; end; end; Image_Set( m, Config_Address, d ); end; procedure Copy_And_Patch( var Old : Pic_Image; var Result : Pic_Image; List : Patch_List; Target : target_type; var Preserved : Pic_Image; var Do_Preserve : boolean ); var Fuses : Word; begin Result := Old; Patch_List_Apply( Result, List ); Patch_Fuses( Result ); Patch_Protection( Result, Protection ); Fuses := Image_Get( Result, $2007 ); Fuses := Fuses and ( $3FFF xor TargetInfo[ Target ].Fixed_Zero ); Fuses := Fuses or TargetInfo[ Target ].Fixed_One; Image_Set( Result, $2007, Fuses ); if Do_Preserve then begin Image_Set( Result, $3FF, Image_Get( Preserved, $3FF )); Fuses := Image_Get( Result, $2007 ) and $0FFF; Fuses := Fuses or (Image_Get( Preserved, $2007 ) and $F000); Image_Set( Result, $2007, Fuses ); { writeln;writeln( '===>', HexWord( Fuses )); } end; end; { read image m from hex file name } procedure ReadHexImage( var m : Pic_Image; name : string ); var f : text; var c : char; var s, n, a, t, i : word; var line : integer; begin Log( 'read hex file ' + name + ' start' ); Image_Clear( m ); assign( f, name ); {$I-} reset( f ); {$I+} if IOResult <> 0 then begin assign( f, name + '.hex' ); {$I-} reset( f ); {$I+} if IOResult <> 0 then begin Fatal( 'file ' + name + ' not found' ); end; end; line := 1; repeat read( f, c ); if c <> ':' then begin fatal( 'hex file format error c=' + HexChar( c ) ); end; s := 0; n := ReadHexByte( f, s ); a := swap( ReadHexWord( f, s )) div 2; t := ReadHexByte( f, s ); if t = 0 then begin for i := 1 to n div 2 do begin Image_Set( m, a, ReadHexWord( f, s )); inc( a ); end; a := ReadHexByte( f, s ); if s <> 0 then begin fatal( 'hex file checksum error in line ' + DecInt( line )); end; line := line + 1; end else if t = 4 then begin { ignore } end else begin if t <> 1 then begin fatal( 'hex file type error t=' + HexByte( t )); end; close( f ); if Image_Get( m, Config_Address ) = Dummy_Value then begin writeln( 'no configuration word in hex-file' ); end; Log( 'read hex file ' + name + ' end' ); exit; end; readln(f); until false; end; { write image m to hex file name } procedure WriteHexImage( var m : Pic_Image; name : string; Parts : Image_Part_Set ); var f : text; var c : char; var s, n, a, t, i, j, k : word; var p : Image_Part; begin assign( f, name ); rewrite( f ); for p := code to fuses do if p in Parts then with Image_Info[p] do begin for i := first to Image_Last( p ) do if i mod 8 = 0 then begin j := i; k := j + 7; if k > Image_Last( p ) then k:= Image_Last( p ); s := 0; write( f, ':' ); write( f, HexByte( 2 * ( k - j + 1 ))); accu2( s, 2 * ( k - j + 1 )); write( f, HexWord( 2 * j )); accu2( s, 2 * j ); write( f, '00' ); for n:= j to k do begin write( f, HexWord( swap( Image_Get( m, n )))); accu2( s, Image_Get( m, n )); end; writeln( f, HexByte( 256 - s )); end; end; writeln( f, ':00000001FF' ); close( f ); end; procedure ShowImage( var m : Pic_Image ); var i : longint; begin for i := $0000 to $4fff do if Image_Get( m, i ) <> $ffff then begin writeln( 'image:' + HexWord( i ) + ':' + HexWord( Image_Get( m, i ))); end; end; procedure DumpHexImage( var m : Pic_Image ); var i : longint; var p : Image_Part; var d : word; begin for p := code to fuses do if p <> code_1k then with Image_Info[p] do begin for i:= first to last do begin d := Image_Get( m, i ); if ( d and mask ) <> mask then begin writeln( 'image ' + HexWord( i ) + ':' + HexWord( d )); end; end; end; end; { --------------------------------------------------------------------- } { { programming via WISP { { --------------------------------------------------------------------- } var Device : string; { verify that a compatible WISP programmer is { connected to the currently selected serial port } procedure WispCheck( id : word ); var s, sv : string; begin { just to clear the communication } WaitSendChar( 'v' ); Delay( 100 ); ClearReceive; { check ID } SendReceive( 'q' ); GetResponse( 4, s ); if (id <> 0) and (HexWord( id ) <> s) then begin fatal( 'device request ' + HexWord(id) + ' response ' + s ); end; Log ( 'device request ' + HexWord(id) + ' response ' + s ); { check device type } SendReceive( 't' ); GetLongResponse( 4, Device ); if ( Device<> 'WISP' ) and ( Device <> 'WLdr' ) and ( Device <> 'Wisp628' ) then begin fatal ('Not a WISP, WLdr or Wisp628, type=' + Device ); end; SendReceive( 'v' ); GetLongResponse( 4, sv ); writeln( 'hardware ' + Device + ' ' + sv ); end; { program image m using write command w } procedure WispWrite( var m : Pic_Image; w : string; Parts : Image_Part_Set ); var p : Image_Part; var i,j,a : integer; var d : word; var NotMask : word; var c : char; begin { ShowImage(m); } for p := code to fuses do if p in Parts then begin SendReceive( Start_Command( TargetInfo[ target ].Algorithm, p )); a := Image_Info[p].start; for i := Image_Info[p].first to Image_Info[p].last do begin d := Image_Get( m, i ); if i mod DumpInterval >= 0 then write( 'program ' + Image_Info[p].name + ' i=' + HexWord( i ) + ' d=' + HexWord( d ) + char( 13 ) ); if d <> Dummy_Value then begin while a < i do begin SendReceive( 'i' ); a := a + 1; end; Log( 'program ' + Image_Info[p].name + ' a=' + HexWord( i ) + ' d=' + HexWord( d ) ); NotMask := ( not TargetInfo[ target ].ProtectionMask ) and $3FFF; if ( i = Config_Address ) and ( not TargetInfo[ target ].ReadFuses ) and ((( not d ) and NotMask ) <> 0 ) then begin { program blind: protection enabled, so it might not read back } {writeln; writeln( HexWord( NotMask ) + ' ' + HexWord( not d ) + ' ' + HexWord(( not d ) and NotMask ) );} SendReceive( HexWord( d ) ); ClearReceive; WaitSendChar( w[1] ); end else begin if ( i = Config_Address ) then begin {writeln( 'w-config=' + HexWord( d ) );} end; SendReceive( HexWord( d ) + w[1] ); ReceiveChar( c ); if( c <> upcase( w[1] )) and ( c <> '?' ) then begin fatal( w[1] + ' or ? expected, received ' + c ); end; end; end; end; write( ' ', char( 13 ) ); end; end; { veriy image m against the current content of the target } procedure WispVerify( var m : Pic_Image; Parts : Image_Part_Set ); var p : Image_Part; var i,j,a : integer; var x : string; var data : word; begin for p := code to fuses do if p in Parts then begin SendReceive( Start_Command( TargetInfo[ target ].Algorithm, p )); a := Image_Info[p].start; for i := Image_Info[p].first to Image_Info[p].last do begin data := Image_Get( m, i ); if i mod DumpInterval = 0 then write( 'verify ' + Image_Info[p].name + ' a=' + HexWord( i - Image_Info[p].first ) + ' d=' + HexWord( data ), char( 13 ) ); Log( 'verify ' + Image_Info[p].name + ' a=' + HexWord( i ) + ' d=' + HexWord( data ) ); if data <> Dummy_Value then begin while a < i do begin SendReceive( 'i' ); a := a + 1; end; SendReceive( 'r' ); GetResponse( 4, x ); if ( Image_Info[p].mask and data ) <> ( Image_Info[p].mask and WordHex( x ) ) then begin fatal( 'verify ' + Image_Info[p].name + ' at ' + HexWord( i - Image_Info[p].first ) + ' expected ' + HexWord( data ) + ' found ' + x ); end; end; end; write( ' ', char( 13 ) ); end; end; { read image m from the target } procedure WispRead( var m : Pic_Image; Parts : Image_Part_Set ); var p : Image_Part; var i : integer; var x : string; var a : integer; begin for p := code to fuses do if p in Parts then begin SendReceive( Start_Command( TargetInfo[ target ].Algorithm, p )); a := Image_Info[p].start; for i := Image_Info[p].first to Image_Last( p ) do begin while a < i do begin SendReceive( 'i' ); a := a + 1; { writeln( 'i=', HexWord( i ), ' skip to ', HexWord( a )); } end; SendReceive( 'r' ); GetResponse( 4, x ); Image_Set( m, i, WordHex( x )); if i mod DumpInterval = 0 then write( 'read ' + Image_Info[p].name + ' a=' + HexWord( i - Image_Info[p].start ) + ' d=' + HexWord( Image_Get( m, i )), char( 13 ) ); { write( char(10) ); } Log( 'read ' + Image_Info[p].name + ' a=' + HexWord( i ) + ' d=' + HexWord( Image_Get( m, i )) ); end; write( ' ', char( 13 ) ); end; end; procedure WispReadPreserved( var m : Pic_Image; var Restore : boolean ); var x, y : word; begin if TargetInfo[ target ].Preserve = Preserve_1K then begin WispRead( m, Preserved_Regions_1K ); x := Image_Get( m, $03FF ); y := Image_Get( m, $2007 ); writeln( 'preserve oscal=' + HexWord( x ) + ' bg=' + HexByte( y shr 12 )); exit; end; Restore := false; end; { erase the target } procedure WispErase( var Image : PIC_Image; Restore : boolean ); var Fuses : word; begin SendReceive( Erase_Command( TargetInfo[ target ].Algorithm )); if Restore then begin Fuses := Image_Get( Image, $2007 ) or $01FF; Image_Set( Image, $2007, Fuses ); WispWrite( Image, 'w', Preserved_Regions_1K ); end; end; { put the target in the run mode } procedure WispRun; begin SendReceive( '0000g' ); end; { put the target in the passtrough mode } procedure WispPassthrough( k : string ); begin SendReceive( k ); WaitSendChar( 'p' ); Delay( 5 ); end; { burn the indicated ID } procedure WispBurn( s : string ); begin SendReceive( s + 'u' ); end; function WhichChip( x : word ) : target_type; var t : target_type; begin for t := target_first to target_last do begin if TargetInfo[ t ].IdMask <> 0 then begin if ( x and not TargetInfo[ t ].IdMask ) = TargetInfo[ t ].Id then begin WhichChip := t; exit; end; end; end; WhichChip := pic_unknown; end; procedure WispCheckTarget; var i : integer; var s : string; var x : word; var t : target_type; begin SendReceive( Start_Command( TargetInfo[ target ].Algorithm, fuses )); for i := 1 to 6 do begin SendReceive( 'i' ); end; SendReceive( 'r' ); GetResponse( 4, s ); x := WordHex( s ); t := WhichChip( x ); if target = pic_unknown then begin write( 'target auto-detect ... ' ); if t = pic_unknown then begin writeln( 'Sorry, I can not determine the target chip type.' ); writeln( 'Specify a target on the command line,' + ' or maybe the chip is defect?' ); writeln( 'The Id that is read is ' + HexWord( x ) + '.' ); fatal( 'No target, target is defect, target has no ID or target is not supported' ); end; target := t; writeln( 'target is a ' + TargetInfo[ t ].Name + ' ' + 'rev ' + HexByte( x and TargetInfo[ t ].IdMask )); end else begin writeln( 'target specified as ' + TargetInfo[ Target ].Name ); if ( target = pic_16c84 ) or ( target = pic_16f84 ) then begin if x <> $3FFF then begin writeln( 'Target appears to be a ' + TargetInfo[ t ].Name ); fatal( 'Target does not match command line' ); end; end else begin if target <> t then begin if target = pic_unknown then begin writeln( 'Sorry, I can not determine the target chip type.' ); writeln( 'Maybe the chip defect?' ); writeln( 'The Id that is read is ' + HexWord( x ) + '.' ); fatal( 'No target, target is defect or target is not supported' ); end else begin writeln( 'Target appears to be a ' + TargetInfo[ t ].Name ); fatal( 'Target does not match command line' ); end; end; writeln( 'target rev is ' + HexByte( x and TargetInfo[ t ].IdMask )); end; end; end; { ******************** VCC regulation } const CalFirst = 39; const CalLast = 61; var Calibration : array[ CalFirst .. CalLast ] of byte; var Actuals : array[ 0 .. 80 ] of real; procedure CalibrationInit; var i : integer; begin for i := 0 to 80 do begin Actuals[ i ] := 0.00; end; end; function ReadEeprom( a : byte ) : byte; var s : string; begin SendReceive( HexByte( a ) + 'K' ); GetResponse( 2, s ); ReadEeprom := ByteHex( s ); end; procedure WriteEeprom( a : byte; x : byte ); var s : string; var y : byte; begin SendReceive( HexByte( a ) + HexByte( x ) + 'M' ); y := ReadEeprom( a ); if x <> y then fatal( 'could not write WISP eeprom' ); end; function CalAddress( x : byte ) : byte; begin CalAddress := x - CalFirst + 10; end; procedure ReadCalibration; var i : integer; begin for i := CalFirst to CalLast do begin Calibration[ i ] := ReadEeprom( CalAddress( i ) ); end; end; procedure CheckCalibration; var i : integer; begin for i := CalFirst to CalLast - 1 do begin if Calibration[ i ] = Calibration[ i + 1 ] then begin fatal( 'wisp hardware is not calibrated' ); end; end; end; procedure WriteCalibration; var i : integer; begin for i := CalFirst to CalLast do begin WriteEeprom( CalAddress( i ), Calibration[ i ] ); end; end; const Vcc_5_00 = 40; function BestVcc( f : real ) : byte; var n : integer; begin n := round( 10 * f ); if n < CalFirst then n := CalFirst; if n > CalLast then n:= CalLast; BestVcc := Calibration[ n ]; end; function VccByte( n : integer ) : word; var i,j : integer; var a,b,m : word; begin a := $F; b := $F; m := 1; for j:=1 to 4 do begin case n mod 3 of 0 : b := b xor m; 1 : a := a xor m; 2 : ; end; n := n div 3; m := 2 * m; end; VccByte := 16 * a + b; end; procedure Calibrate; var i, j, n : integer; var s : string; var w : byte; var target, error, largest : real; var f : text; var v : real; begin if Device <> 'WISP' then begin fatal( 'calibration is supported only for a WISP programmer' ); end; writeln( 'VCC calibration:' ); s := ''; while s <> ' ' do begin writeln( 'enter file name to read (space + return for none)' ); readln( s ); if s <> '' then begin assign( f, s ); {$I-} reset( f ); {$I+} if IOResult = 0 then begin for i := 0 to 80 do begin readln( f, w, Actuals[ i ] ); end; close( f ); s := ' '; end; end; end; writeln( 'Enter the actual voltage and press' ); writeln( 'return for each of the 81 settings, ' ); writeln( 'enter space + return to abort the calibration' ); largest := 0.0; for i := 0 to 80 do begin w := VccByte( i ); SendReceive( HexByte( w ) + 'z' ); write( i : 2 ); write( ' ' ); write( Actuals[ i ] : 4 : 2 ); write( ' : ' ); readln( s ); if s = ' ' then begin writeln( 'calibration aborted' ); exit; end; val( s, v, n ); if n = 0 then begin Actuals[ i ] := v; end; end; largest := 0; for i := CalFirst to CalLast do begin n := 0; target := ( 1.0 * i ) / 10; error := 100.0; for j := 0 to 80 do begin if error > abs( Actuals[ j ] - target ) then begin n := j; error := abs( Actuals[ n ] - target ); end; end; calibration[ i ] := n; write( target : 4 : 2 ); write( ' ' + HexByte( Calibration[ i ] ) ); write( '=>' ); write( Actuals[ n ] : 4 : 2 ); write( ' E=' ); write( error : 4 : 2 ); if ( i - CalFirst + 1) mod 3 = 0 then begin writeln end else begin write( ' ' ); end; if error > largest then begin largest := error; w := i; end; end; write( 'Emax=' ); write( largest : 4 : 2 ); write( ' for ' ); writeln( ( 1.0 * w ) / 10 : 4 : 2 ); s := ''; while s <> ' ' do begin writeln( 'enter file name to write (space + return for none)' ); readln( s ); if s <> '' then begin assign( f, s ); {$I-} rewrite( f ); {$I+} if IOResult = 0 then begin for i := 0 to 80 do begin writeln( f, i : 2, ' ', Actuals[ i ] ); end; close( f ); s := ' '; end; end; end; WriteCalibration; write( 'Calibration done' ); end; const VccHigh : word = Vcc_5_00; const VccLow : word = Vcc_5_00; procedure WispTwiceVerify( var m : Pic_Image; Parts : Image_Part_set ); begin if ( VccHigh = Vcc_5_00 ) and ( VccLow = Vcc_5_00 ) then begin WispVerify( m, Parts ); end else begin Show( 'Check with Vcc Low' ); SendReceive( HexByte( VccByte( VccLow ) ) + 'z' ); delay( 2000 ); WispVerify( m, Parts ); Show( 'Check with Vcc High' ); SendReceive( HexByte( VccByte( VccHigh ) ) + 'z' ); delay( 2000 ); WispVerify( m, Parts ); SendReceive( '00z' ); end; end; { ******************** target testing } { test the target with an image constructed { from x and y, using write command w } procedure WispOneTest(var m : Pic_Image; s, w : string; x, y : word ); begin writeln( s ); Image_Set_All( m, x, y ); Image_Set( m, Config_Address, Dummy_Value ); WispWrite( m, w, [ code ] ); WispTwiceVerify( m, [ code ] ); end; { perform a few tests on the target } procedure WispTest( w : string ); var p : ^Pic_Image; begin new(p); WispOneTest( p^, '1,2,3...', w, 1, 1 ); WispOneTest( p^, '1,4,7...', w, 1, 3 ); WispOneTest( p^, 'all a5a5', w, $a5a5, 0 ); WispOneTest( p^, 'all 5a5a', w, $5a5a, 0 ); WispOneTest( p^, 'all 0', w, $0, 0 ); WispOneTest( p^, 'all ffff', w, $ffff, 0 ); end; { ******************** on-line help } const Help_Lines = 44; type Help_String = string[ 39 ]; const Help_Text : array[ 1..Help_Lines ] of Help_String = ( 'BEEP : beep at end ', 'BURN n : set device id ', 'CALIBRATE : calibrate VCC ', 'CHECK : buffer against target ', 'DTR x : set DTR line ', 'DUMP : dump buffer content ', 'ERASE : erase targets ', 'FLUSH : flush logging ', 'FUSES x : x=IGNORE, FILE or value ', 'GET : target to buffer ', 'GO f : erase, write f, check, run', 'HEX : hex values (TALK,TERM,TTY)', 'ID n : use device with ID n ', 'LAZY : use lazy programming ', 'LOAD f : file f to buffer ', 'LOG f : log to file f ', 'NORESTORE : do not restore calibr.etc.', 'PASS m : enable passthrough ', ' m=B67T, B67I, AUXT, AUXI ', 'PATCH OFF : clear patch list ', 'PATCH a=v : patch address a, value v ', 'PAUSE m : print m, wait for return ', 'PORT P : use serial port p ', 'PORT b : use baudrate b ', 'PROTECT x : x=ON, OFF or FILE ', 'PUT : buffer to target ', 'READ f : get, save f ', 'RTS x : set RTS line ', 'RUN : put target in run mode ', 'SAVE f : buffer to file ', 'SELECT x : x[i] from +-CDFA ', 'TALK : talk to wisp ', 'TARGET x : specify target chip ', 'TERM b : TTY @ line, baudrate b ', 'TEST : test programmability ', 'TIME : show current time ', 'TTY b : TTY to target, baudrate b ', 'TTY [m] b : idem, for m see PASS ', 'VCC l h : set verify voltages ', 'VERBOSE : enable screen logging ', 'VERIFY f : load f, check ', 'WAIT n : wait (at least) N ms ', 'WRITE f : load f, put ', ' '); procedure Help; var i : integer; begin assign( output, ''); rewrite( output ); writeln( output, 'wisptool ' + version + '; see http://www.voti.nl/wisptool '); writeln(output, 'commands:' ); for i := 1 to Help_Lines div 2 do begin writeln( output, Help_Text[ i ] + ' ' + Help_Text[ i + Help_Lines div 2 ] ); end; end; { ******************** command line handling } var ComPort : integer; var WispID : word; var WriteCmd : string; var Image : ^Pic_Image; var Image2 : ^Pic_Image; var Preserved_Image : ^Pic_Image; var Patches : Patch_List; var Parts : Image_Part_Set; const InitDone : boolean = false; const TargetDone : boolean = false; const Restore : boolean = true; const Wbus_Baudrate : integer = 19200; { init using WISP on ComPort with ID WispID } procedure WispInit( t : boolean ); begin if not InitDone then begin InitPort( ComPort, Wbus_Baudrate ); ResetTarget; LineBreak; Log( 'wake up device ' + HexWord( WispID ) ); WaitIgnoreSendString( HexWord( WispID ) + 'h' + '0000'); ClearReceive; WispCheck( WispID ); InitDone := true; end; if t and ( not TargetDone ) then begin WispCheckTarget; if ( Device = 'WISP' ) and ( TargetInfo[ target ].Algorithm <> Algorithm_PIC16 ) then begin fatal( 'this target can not be programmed with a WISP programmer' ); end; if Restore then begin WispReadPreserved( Preserved_Image^, Restore ); end; TargetDone := true; end; end; { verify availability of a next parameter and return it { increment i } function NextParameter( var i : integer ) : string; begin if i = paramcount then begin fatal( 'missing argument for ' + UpStr( paramstr( i ) ) ); end; i := i + 1; NextParameter := paramstr( i ); end; function Polarity( s : string; var x : boolean ) : boolean; begin if ( s = '+' ) or ( s = 'ON' ) then begin x := true; Polarity := true; end else if ( s = '-' ) or ( s = 'OFF' )then begin x := false; Polarity := true; end else begin Polarity := false; end; end; { handle parameter i } procedure HandleParameter( var i : integer ); var p, s, k : string; var s1, s2 : string; var j, c : integer; var v, v1, v2 : longint; var x : real; var d : word; var t : target_type; var sign : integer; var flag : boolean; begin p := UpStr( paramstr( i ) ); if p = 'BEEP' then begin BeepEnable := true; exit; end; if p = 'BURN' then begin s := UpStr( NextParameter( i ) ); if ( length( s ) <> 4 ) then begin fatal( 'BURN argument must be 4 hex chracters' ); end; for j := 1 to 4 do case upcase( s[ j ] ) of '0'..'9', 'A'..'F' : ; else fatal( 'BURN argument must be 4 hex chracters' ); end; WispInit( false ); WispBurn( s ); exit; end; if p ='CALIBRATE' then begin WispInit( false ); Calibrate; InitDone := false; SendReceive( '00z' ); exit; end; if p = 'CHECK' then begin WispInit( true ); WispTwiceVerify( Image^, Parts ); exit; end; if p = 'DTR' then begin s := NextParameter( i ); if Polarity( UpStr( s ), flag ) then begin DTR_specified := true; set_DTR( flag ); end else begin fatal( 'invalid DTR argument : ' + s ); end; exit; end; if p = 'DUMP' then begin DumpHexImage( Image^ ); exit; end; if p = 'ERASE' then begin WispInit( true ); WispErase( Preserved_Image^, Restore ); exit; end; if p = 'FLUSH' then begin FlushEnable := true; exit; end; if p = 'FUSES' then begin s := NextParameter( i ); if UpStr( s ) = 'IGNORE' then begin Include_Fuses := false; end else if UpStr( s ) = 'FILE' then begin Include_Fuses := true; Fixed_Fuses := false; end else begin val(s, v, c ); if (c <> 0) or (v < 0) or (v > $ffff) then begin fatal( 'FUSES argument not valid (must be IGNORE, FILE or a value' ); end; Include_Fuses := true; Fixed_Fuses := true; Fuses_Value := v; end; exit; end; if p = 'GET' then begin WispInit( true ); WispRead( Image^, Parts ); exit; end; if p = 'GO' then begin WispInit( true ); WispErase( Preserved_Image^, Restore ); ReadHexImage( Image^, NextParameter( i ) ); { writeln('config was ' + HexWord( Image[ ConfigAddress ] ) ); } Copy_And_Patch( Image^, Image2^, Patches, target, Preserved_Image^, Restore ); WispWrite( Image2^, WriteCmd, Parts ); WispTwiceVerify( Image2^, Parts ); WispRun; exit; end; if p = 'HEX' then begin show_hex := true; exit; end; if p = 'ID' then begin s := NextParameter( i ); val(s, v, c ); if (c <> 0) or (v < 0) or (v > $ffff) then begin fatal( 'ID argument not valid' ); end; WispID := v; exit; end; if p = 'LAZY' then begin WriteCmd := 'l'; exit; end; if p = 'LOAD' then begin ReadHexImage( Image^, NextParameter( i ) ); exit; end; if p = 'LOG' then begin LogOpenFile( NextParameter( i ) ); exit; end; if p = 'NORESTORE' then begin Restore := false; exit; end; if p = 'PASS' then begin s := UpStr( NextParameter( i ) ); k := '0000'; if s = 'B67T' then begin k := '0000'; ModeTextIndex := 1; end else if s = 'B67I' then begin k := '0001'; ModeTextIndex := 2; end else if s = 'AUXT' then begin k := '0010'; ModeTextIndex := 3; end else if s = 'AUXI' then begin k := '0011'; ModeTextIndex := 4; end; WispInit( false ); writeln( 'passthrough mode ' + ModeText[ ModeTextIndex ] ); WispPassthrough( k ); InitDone := false; exit; end; if p = 'PATCH' then begin s := UpStr( NextParameter( i ) ); if s = 'OFF' then begin Patches := nil; exit; end; Pivot( s, '=', s1, s2 ); val(s1, v1, c ); if (c <> 0) or (v1 < 0) or (v1 > $ffff) then begin fatal( 'PATCH address argument not valid' ); end; val(s2, v2, c ); if (c <> 0) or (v2 < 0) or (v2 > $ffff) then begin fatal( 'PATCH value argument not valid' ); end; Patch_List_Add( Patches, v1, v2 ); exit; end; if p = 'PAUSE' then begin s := NextParameter( i ); writeln( '>>> ' + s ); writeln( '(press return to continue)' ); readln; exit; end; if p = 'PORT' then begin s := UpStr( NextParameter( i ) ); if ( substr( s + ' ', 1, 3 ) = 'COM' ) then begin delete( s, 1, 3 ); end; val(s, v, c ); if (c <> 0) or (v < 1) then begin fatal( 'x PORT argument not valid' ); end; if v <= 8 then begin ComPort := v; InitDone := false; exit; end else begin Wbus_Baudrate := v; exit; end end; if p = 'PROTECT' then begin s := UpStr( NextParameter( i ) ); if s = 'ON' then begin Protection := always; exit; end; if s = 'OFF' then begin Protection := never; exit; end; if ( s = 'FILE' ) or ( s = 'IMAGE' ) then begin Protection := hex; exit; end; fatal( 'invalid protection argument ' + s ); end; if p = 'PUT' then begin WispInit( true ); Copy_And_Patch( Image^, Image2^, Patches, target, Preserved_Image^, Restore ); WispWrite( Image2^, WriteCmd, Parts ); exit; end; if p = 'READ' then begin WispInit( true ); WispRead( Image^, Parts ); Copy_And_Patch( Image^, Image2^, Patches, target, Preserved_Image^, Restore ); WriteHexImage( Image2^, NextParameter( i ), Parts ); exit; end; if p = 'RTS' then begin s := UpStr( NextParameter( i ) ); if Polarity( s, flag ) then begin set_RTS( flag ); end else begin fatal( 'invalid RTS argument : ' + s ); end; exit; end; if p = 'RUN' then begin WispInit( false ); WispRun; exit; end; if p = 'SAVE' then begin Copy_And_Patch( Image^, Image2^, Patches, target, Preserved_Image^, Restore ); WriteHexImage( Image2^, NextParameter( i ), Parts ); exit; end; if p = 'SELECT' then begin s := UpStr( NextParameter( i ) ); sign := 1; for j := 1 to length( s ) do begin case s[ j ] of 'A' : Add_Or_Remove( Parts, sign, All_Parts ); 'C' : Add_Or_Remove( Parts, sign, [ code ] ); 'D' : Add_Or_Remove( Parts, sign, [ data ] ); 'F' : Add_Or_Remove( Parts, sign, [ fuses ] ); '+' : sign := +1; '-' : sign := -1; else fatal( 'SELECT argument contains invalid character(s)' ); end; end; exit; end; if p = 'TALK' then begin WispInit( false ); EmulateTTY( ComPort, 'chat with device', 19200 ); InitDone := false; exit; end; if p = 'TARGET' then begin s := UpStr( NextParameter( i ) ); if s = 'AUTO' then begin Target := pic_unknown; exit; end; for t := target_first to target_last do begin if ( UpStr( TargetInfo[ t ].Name ) = s ) or ( UpStr( TargetInfo[ t ].Abbreviation ) = s ) then begin Target := t; exit; end; end; fatal( 'invalid target argument ' + s ); end; if p = 'TERM' then begin s := UpStr( NextParameter( i ) ); if pos( 'K', s ) = 0 then begin val(s, v, c ); if (c <> 0) or (v < 0) then begin fatal( 'TERM argument not valid' ); end; end else begin val(substr(s, 1, pos('K',s) - 1), v, c); if (c <> 0) or (v < 0) then begin fatal( 'TERM argument not valid' ); end; val(substr(s + '000', pos('K',s) + 1, 3 ), v2, c); if (c <> 0) or (v2 < 0) then begin fatal( 'TERM argument not valid' ); end; v := 1000 * v + v2; end; EmulateTTY( ComPort, 'raw serial line', v ); exit; end; if p = 'TEST' then begin WispInit( true ); WispTest( WriteCmd ); exit; end; if p = 'TIME' then begin Show( TimeStr ); exit; end; if p = 'TTY' then begin s := UpStr( NextParameter( i ) ); k := '0000'; if s = 'B6T' then begin k := '0000'; s := UpStr( NextParameter( i ) ); ModeTextIndex := 1; end else if s = 'B6I' then begin k := '0001'; s := UpStr( NextParameter( i ) ); ModeTextIndex := 2; end else if s = 'AUXT' then begin k := '0002'; s := UpStr( NextParameter( i ) ); ModeTextIndex := 3; end else if s = 'AUXI' then begin k := '0003'; s := UpStr( NextParameter( i ) ); ModeTextIndex := 4; end; if pos( 'K', s ) = 0 then begin val(s, v, c ); if (c <> 0) or (v < 0) then begin fatal( 'TTY argument not valid' ); end; end else begin val(substr(s, 1, pos('K',s) - 1), v, c); if (c <> 0) or (v < 0) then begin fatal( 'TTY argument not valid' ); end; val(substr(s + '000', pos('K',s) + 1, 3 ), v2, c); if (c <> 0) or (v2 < 0) then begin fatal( 'TTY argument not valid' ); end; v := 1000 * v + v2; end; { writeln( 'k="' + k + '"' ); } WispInit( false ); WispPassthrough( k ); EmulateTTY( ComPort, ModeText[ ModeTextIndex ], v ); InitDone := false; exit; end; if p = 'VCC' then begin WispInit( false ); ReadCalibration; CheckCalibration; s := UpStr( NextParameter( i ) ); val( s, x, d ); if d <> 0 then fatal( 'first VCC argument not valid' ); VccLow := BestVcc( x ); s := UpStr( NextParameter( i ) ); val( s, x, d ); if d <> 0 then fatal( 'second VCC argument not valid' ); VccHigh := BestVcc( x ); exit; end; if p = 'VERBOSE' then begin DumpInterval := 1; VerboseEnable := true; exit; end; if p = 'VERIFY' then begin ReadHexImage( Image^, NextParameter( i ) ); WispInit( true ); WispTwiceVerify( Image^, Parts ); exit; end; if p = 'WAIT' then begin s := UpStr( NextParameter( i ) ); val(s, v, c ); if (c <> 0) or (v < 1) then begin fatal( 'WAIT argument not valid' ); end; Delay( v ); exit; end; if p = 'WRITE' then begin ReadHexImage( Image^, NextParameter( i ) ); WispInit( true ); Copy_And_Patch( Image^, Image2^, Patches, target, Preserved_Image^, Restore ); WispWrite( Image2^, WriteCmd, Parts ); exit; end; fatal( 'invalid command ' + paramstr( i ) ); end; procedure test; var s : string; var i,j : integer; var a, b: byte; begin ComPort := 1; VerboseEnable := false; InitPort( ComPort, 57600 ); ClearReceive; GetSX( chr( 0 ) + 'SX-Key', 16, s ); if true then begin for i := 1 to length(s) do begin write( HexChar( s[ i ] ) ); write( ' ' ); end; writeln; end; VerboseEnable := false; ClearReceive; GetSX( chr( 3 ), 2, s ); if true then begin for i := 1 to length(s) do begin write( HexChar( s[ i ] ) ); write( ' ' ); end; writeln; end; VerboseEnable := false; for i:= 1 to 2048 + 3 + 16 do begin delay( 100 ); if false then ClearReceive; GetSX( chr(1), 4, s ); if false then begin write( 'i=',i,' '); for j := 1 to length(s) do begin write( HexChar( char( 256 - ord( s[ j ] ) ) ) ); write( ' ' ); end; writeln; end else begin if odd(i) then begin a := 256 - ord( s[ 2 ] ); end else if false then begin b := 256 - ord( s[ 2 ] ); write( ' ' ); write( HexChar( char( b ) ) ); write( HexChar( char( a ) ) ); if i mod 16 = 0 then writeln; end else begin if i mod 10 = 0 then begin write( i,' ', chr(13) ); end; end; end; end; end; { main: init, handle all parameters } var i : integer; begin CalibrationInit; ComPort := 1; WispID := $0000; WriteCmd := 'w'; i := 1; Patches := nil; Protection := hex; Parts := All_Parts; new( Image ); new( Image2 ); new( Preserved_Image ); Image_Clear( Image^ ); Image_Clear( Preserved_Image^ ); set_RTS( false ); set_DTR( false ); if paramcount = 0 then begin Help; end else begin writeln('wisptool ' + version ); while i <= paramcount do begin HandleParameter( i ); i := i + 1; end; if LogOpen then close( LogFile ); BeepOk; writeln( 'OK' ); end; end.