/* PUBIO = PIC16F145x USB Bootloader Input/Output
Dieser Mikrocontroller bietet USB Full-Speed mit gerade mal
zwei extern anzuschließenden Kondensatoren!
Und es gibt ihn im bastelfreundlichen Durchsteckgehäuse.
Er hat viele Möglichkeiten, u.a. einen 10-bit-A/D-Wandler
und ist ansonsten mit ATtiny44 oder ATtiny2313 vergleichbar.
Die erweiterte Version des 512 Programmworte großen Urladers
bietet zwei neue Befehle:
* Byte lesen (nach Senden einer 2-Byte-Adresse, LSB zuerst)
* Byte schreiben (nach Senden einer 2-Byte-Adresse, LSB zuerst, und dem Datenbyte)
Damit kann ein PIC16F145x als einfaches Laborautomatisierungssystem
verwendet werden, ohne ein einziges Byte Firmware schreiben zu müssen.
Für alles was wenig zeitkritisch ist reicht das vollkommen aus.
Die Namen und Adressen der „Special Function Register“ der (größten)
PIC16F1459 sind ins Programm eingebaut, für die notwendigen Daten
muss man im Datenblatt nachgucken.
Wenn sinnvoll werden Bits und Bitnamen angezeigt,
diese können auch einzeln manipuliert werden:
Mausklick oder mit TAB fokussieren und Leertaste.
Dieses Programm dient zum Herumexperimentieren mit dieser Schnittstelle.
Der Einsatz erfolgt dann in einem problemangepassten Programm,
bspw. in LabVIEW.
Für ambitioniertere Aufgaben ist es jedoch sinnvoll,
echte Firmware zu entwickeln und ggf. über dasselbe Interface zu brennen.
Da sowohl Urlader als auch Firmware die gleiche USB-Schnittstelle
und das gleiche COM-Port verwenden können,
kann dieser Vorgang auch „in system“ erfolgen.
Die Firmware muss ab Programm-Adresse 0x200 beginnen,
und der Anwender-Interrupt startet entsprechend auf 0x204.
Bei Verwendung von SDCC als C-Compiler muss ein eigens angepasstes
Linker-Skript (*.lkr) zum Einsatz kommen, damit die Startadresse stimmt.
Sowie (leider) Zwischen-Assembler-Dateien gepatcht werden.
Siehe http://www.tu-chemnitz.de/~heha/ewa/PIC16F145x-Urlader/.
Den Urlader als COM-Port-Treiber weiter zu verwenden ist optional.
Dazu gibt es „magische Programm-Adressen“ bei 0x001, 0x002, 0x003
sowie 0x202. Siehe Quelltext „bootCDC.asm“.
Der Stil meiner klitzekleinen Windows-Programme ist immer gleich.
Hier nachlesen: http://www.tu-chemnitz.de/~heha/hs/mein_msvc.htm
Henrik Haftmann, November 2018
+210515 High-DPI-Manifest
!210516 MSVC2008: /O1 generiert Murks bei 32 Bit: Crash bei bootCDC-Firmware
ohne ID-Bit-Unterstützung; /Os verwenden (ungelöst, ich nehme MSVC6)
-210517 Symboltabelle geändert, Little/Big Endian getrennt
-210519 Flash-Dateiname mit .hex wenn nicht angegeben
-210606 Register-Namensanzeige, Anzeige der linearen und Bank-Adresse bei RAM
+210608 Buffer-Descriptor-Table-Anzeige
+210609 Optionen beim Flash-Speichern
+210627 Optionen beim Programmieren und Vergleichen
*/
#define _WIN32_WINNT 0x0600 // OpenFileDialog mit PlacesBar; SplitButton
#include <windows.h>
#include <windowsx.h>
#include <shlwapi.h>
#include <setupapi.h>
#include <devguid.h>
#define T(x) TEXT(x) // Schreibfaulheit…
#define elemof(x) (sizeof(x)/sizeof(*(x))) // Elemente eines Arrays
#define nobreak // expliziter Durchlauf bei switch/case
#if _MSC_VER >= 1400
# include <intrin.h>
# define memset(a,b,c) __stosb((PBYTE)(a),b,c)
# define memcpy(a,b,c) __movsb((PBYTE)(a),(PBYTE)(b),c)
#else
# ifndef SetClassLongPtr
# define SetClassLongPtr SetClassLong
# define GCLP_HICON GCL_HICON
# endif
# define INT_PTR INT
# define LONG_PTR LONG
# pragma intrinsic(memset,memcpy)
__forceinline void __stosw(WORD*d, WORD w, size_t l) {
_asm mov edi,d
_asm mov ax,w
_asm mov ecx,l
_asm rep stosw
}
#endif
#ifdef _DEBUG
static void _cdecl debugout(const TCHAR*fmt,...) {
va_list va;
va_start(va,fmt);
TCHAR s[256];
if (IS_INTRESOURCE(fmt)) {
TCHAR t[256];
LoadString(0,(UINT)fmt,t,elemof(t));
fmt=t;
}
wvnsprintf(s,elemof(s),fmt,va);
va_end(va);
OutputDebugString(s);
}
# define debug(x) debugout x
# define assert(x) if (!(x)) debugout(T("%hs(%d): Assert failed: %hs\n"),__FILE__,__LINE__,#x)
#else
# define debug(x)
# define assert(x)
#endif
extern char const pic16f1459sym[];
extern char const pic16f1459bits[];
static struct Config{
short posX,posY;
BYTE ComNr; // Nullbasierte COM-Portnummer
BYTE flags; // Bit 0: <addr> als SFR bei PIC16F1454 vorhanden
// Bit 1: <addr> als SFR bei PIC16F1455 vorhanden
// Bit 2: <addr> als SFR bei PIC16F1459 vorhanden
// Bit 3: <addr> beim Lesen veränderlich; nicht automatisch lesen, Suffix „v“
// Bit 4: <addr> bezeichnet 16-Bit-Wert Big Endian, Suffix „W“
// Bit 5: <addr> bezeichnet 16-Bit-Wert Little Endian, Suffix „w“
// Bit 7: <addr> als Zahl gegeben, sonst als SFR
WORD addr; // Zuletzt benutzte Adresse
WORD data; // Zuletzt geschriebenes Datenbyte oder Wort
BYTE saveflags;// Infobits für Hex-Datei-Schreiben (in der Reihenfolge der Button-IDs)
BYTE progflags;// Infobits für Programmierfunktion
BYTE veriflags;// Infobits, was zu vergleichen ist
BYTE read_time;// Timer-Wert fürs automatische Lesen in 10 ms)
WORD datamax() const {return flags&0x30?0xFFFF:0xFF;}
WORD linaddr() const;
WORD bdt() const {return linaddr()-0x2000;}
}config;
// Die Angabe auf Seite 302 (USB RAM) ist falsch!
// Richtig ist, der Single-Port-RAM beginnt bei 0x21F0 und endet bei 0x23EF,
// liegt auf krummen Adressen.
// Der linear adressierbare Dual-Port-RAM liegt bei 0x2000..0x21EF, die letzten 16 Bytes
// befinden sich (nur) im bank-adressierbaren Bereich 0x70..0x7F, damit's 512 Bytes werden.
WORD Config::linaddr() const {
if (addr>=0x1000) return addr; // nicht umrechnen
BYTE b=addr>>7, ba=addr&0x7F; // aufteilen: Bank (0..31), Adresse (0..127)
if (ba>=0x70) return 0; // Letzte 16 Bytes des Dual-Port-RAM (via BDT nutzbar oder nicht??)
if (ba<0x20) return 0; // SFR-Bereich nicht umrechenbar
if (b==31) return 0; // Bank 31 nicht umrechenbar
return 0x2000+b*80+ba-0x20; // nur bis 0x23EF mit RAM belegt
}
static TCHAR HexFileName[260];
static HWND MainWnd;
static TCHAR MBoxTitle[64];
static int vMBox(UINT id, UINT type, va_list va) {
TCHAR s[1000],t[1000];
LoadString(0,id,t,elemof(t));
wvnsprintf(s,elemof(s),t,va);
return MessageBox(MainWnd,s,MBoxTitle,type);
}
static int _cdecl MBox(UINT id, UINT type, ...) {
va_list va;
va_start(va,type);
int r=vMBox(id,type,va);
va_end(va);
return r;
}
static void LoadSettings() {
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\pubio"),0,KEY_QUERY_VALUE,&hKey)) return;
DWORD len=sizeof Config;
RegQueryValueEx(hKey,T("Config"),0,0,(BYTE*)&config,&len);
len=sizeof HexFileName;
RegQueryValueEx(hKey,T("HexFileName"),0,0,(BYTE*)HexFileName,&len);
RegCloseKey(hKey);
}
static void SaveSettings() {
HKEY hKey;
// Schlüssel-Zweig öffnen
if (RegCreateKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\pubio"),0,
0,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE,0,&hKey,0)) return;
// eine Beschreibung schreiben (damit der Anwender weiß, worum es geht)
RegSetValue(hKey,0,REG_SZ,MBoxTitle,(lstrlen(MBoxTitle)+1)*sizeof(TCHAR));
RegSetValueEx(hKey,T("Config"),0,REG_BINARY,(PBYTE)&config,sizeof config);
RegSetValueEx(hKey,T("HexFileName"),0,REG_SZ,(PBYTE)HexFileName,(lstrlen(HexFileName)+1)*sizeof(TCHAR));
RegCloseKey(hKey);
}
BOOL EnableDlgItem(HWND Wnd,UINT id,BOOL e) {
return EnableWindow(GetDlgItem(Wnd,id),e);
}
static HANDLE hCom;
static BYTE detectedChip; // 0, 4, 5 oder 9
static bool OpenComm() {
if (hCom) return true;
TCHAR s[12];
wnsprintf(s,elemof(s),T("\\\\.\\COM%u"),config.ComNr+1);
HANDLE h=CreateFile(s,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0);
if (h==INVALID_HANDLE_VALUE) {
debug((T("Could not open %s, error %d (0x%X)\n"),s,GetLastError(),GetLastError()));
return false;
}
hCom=h;
return true;
}
static void CloseComm() {
if (!hCom) return;
if (CloseHandle(hCom)) hCom=0;
SetDlgItemText(MainWnd,106,0);
EnableDlgItem(MainWnd,4,false); // Knopf "Lesen"
EnableDlgItem(MainWnd,5,false); // Knopf "Schreiben"
for (UINT i=8; i<12; i++) EnableDlgItem(MainWnd,i,false);
detectedChip=0;
}
// Weil von den anderen COM-Ports nichts zurückkommt, mit asyncronen Transfers arbeiten.
// COMM-Timeouts würden auf ähnlich schlampig implementierter USB-Mikrocontroller-Firmware
// nicht so zuverlässig funktionieren, denn da muss die USB-CDC Firmware (sicherlich) mitarbeiten.
static DWORD WriteComm(const void*b, DWORD len, DWORD to=100) {
OVERLAPPED o;
ZeroMemory(&o,sizeof o);
DWORD bw=0;
HANDLE h=hCom;
if (!WriteFile(h,b,len,&bw,&o)
&& GetLastError()==ERROR_IO_PENDING)
if (!WaitForSingleObject(h,to)) GetOverlappedResult(h,&o,&bw,FALSE);
else CancelIo(h);
return bw;
}
static DWORD ReadComm(void*b, DWORD len, DWORD to=100) {
OVERLAPPED o;
ZeroMemory(&o,sizeof o);
DWORD br=0;
HANDLE h=hCom;
if (!ReadFile(h,b,len,&br,&o)
&& GetLastError()==ERROR_IO_PENDING)
if (!WaitForSingleObject(h,to)) GetOverlappedResult(h,&o,&br,FALSE);
else CancelIo(h);
return br;
}
namespace pubio{
static int W(const void*buf,DWORD len) {
DWORD b=WriteComm(buf,len);
if (b!=len) {CloseComm(); return -3;}
BYTE r;
b=ReadComm(&r,1);
if (b!=1) {CloseComm(); return -2;}
return r;
}
// wie oben aber mit Fehlerkode-Umwandlung
static int We(const void*buf,DWORD len) {
int r=W(buf,len); // es muss 1 für Okay von der Firmware zurückkommen
if (r!=1) r+=10; // 2,3,4 in String-ID umwandeln (Murks!)
return r;
}
// Byte aus PIC-Adressraum lesen
static int Read(WORD a) {
if (!OpenComm()) return -7;
return W(&a,2);
}
// Byte in PIC-Adressraum schreiben
static int Write(WORD a,BYTE b) {
if (!OpenComm()) return -7;
struct{
WORD a;
BYTE b;
}block={a,b};
return We(&block,3); // es muss 1 für Okay von der Firmware zurückkommen
}
// Äpp anspringen
static int JumpToApp() {
BYTE b='R';
int r=We(&b,1); // Reset ausgeben, Firmware starten, muss 1 liefern für Erfolg
if (r!=1) return r;
CloseComm();
return r;
}
}//namespace pubio
static BYTE HexData[0x4000]; // 16 Kilobyte (8K×14bit) Flash-Daten für den Controller
#define HexDataW ((WORD*)HexData)
static BYTE IdSpace[64];
/*Wort-Adressen: 0x8000: IDLOC0
0x8001: IDLOC1
0x8002: IDLOC2
0x8003: IDLOC3
0x8004: frei, 0x3FFF
0x8005: Revision-ID
0x8006: Chip-ID
0x8007: CONFIG1
0x8008: CONFIG2
0x8009..0x801F: frei, 0x0000
*/
#define IdSpaceW ((WORD*)IdSpace)
/*******************
* Hex-Datei lesen *
*******************/
struct get{ // Speicherlese-Objekt (für die Hex-Daten)
const char*p;
const char*const e;
static int line;
get(const void*q,DWORD len):p((const char*)q),e((const char*)q+len) {line=1;};
int chr() {if (p==e) return -9; byte r=*p++; if (r==10) ++line; return r;}
int nib();
int byt();
};
int get::nib() { // ohne Kleinbuchstaben
int c=chr();
if (c<0) return c; // EOF
if (c<'0' || c>'9' && c<'A' || c>'F') return -8; // Falsches Zeichen
c-='0'; if (c>9) c-=7;
return c; // 0..15
}
int get::byt() {
int c1=nib();
if (c1<0) return c1;
int c2=nib();
if (c2<0) return c2;
return c1<<4|c2; // 0..255
}
const WORD DefFlash=0x3FFF;
static bool haveID; // Hex-Datei enthielt ID-Bits (sonst unverändert lassen)
int get::line;
// Intel-Hex-Datei einlesen
// TODO: Einige Fehler ignorierbar machen
// checkChpCfg: Bit 0: Urlader-Bits zulassen
// Bit 5: Chip-Vergleich aktivieren
// Bit 6: Config-Vergleich aktivieren
static int ParseHexFile(get gp,BYTE checkChpCfg) {
__stosw(HexDataW,DefFlash,sizeof HexData>>1);
if (!detectedChip) checkChpCfg&=~0x60; // kann ID-Space nicht vergleichen (alter Urlader)
bool havedata=false;
haveID=false;
DWORD ha=0; // High-Adresse
int c;
for (;;) {
do{
c=gp.chr();
if (c<0) return c; // Endekennung fehlt
}while(c!=':'); // Anfang von Hexdaten suchen
BYTE line[32+5];
c=gp.byt();
if (c<0) return c; // Keine Hexziffer
if (c>32) return -5; // Zeile zu lang
BYTE cs=line[0]=(BYTE)c;
for (int i=1;i<line[0]+5;i++) { // Bytezeile einlesen
c=gp.byt();
if (c<0) return c;
line[i]=(BYTE)c;
cs+=c;
}
if (cs) return -4; // Prüfsummen-Fehler
DWORD a=ha+MAKEWORD(line[2],line[1]); // Startadresse
switch (line[3]) {
case 0: { // Datenzeile
for (int i=0; i<line[0]; i++) {
DWORD aa=a+i; // Byteadresse
BYTE b=line[i+4]; // Zu programmierendes Byte
if (aa&1) b&=HIBYTE(DefFlash); // 2 MSB entfernen, falls irrtümlich gesetzt
if (aa>=0x10000) { // Microchips ID-Adressbereich × 2
aa-=0x10000;
DWORD wa=aa>>1; // Wortadresse
switch (wa) {
case 0: case 1: case 2: case 3: IdSpace[aa]=b; haveID=true; break;
case 5: break; // Revision-ID ignorieren
case 6: if (checkChpCfg&0x20 && IdSpace[aa]!=b) return -23; break; // Unveränderlich
case 7:
case 8: if (checkChpCfg&0x40 && IdSpace[aa]!=b) return -24; break; // Passt nicht
default: return -20; // reserviertes Word (4) bzw. Überschüssige Konfigurationsdaten
}
}else{
if (!(checkChpCfg&1) && aa<1024) return -17; // Hex-Datei überschreibt Urlader
if (aa>=sizeof HexData) return -21; // Überschüssige Daten
HexData[aa]=b; // einzelbyteweise übertragen
havedata=true;
}
}
}break;
case 1: if (!havedata) return c; return 0; // Ende-Record
case 2: ha=DWORD(MAKEWORD(line[5],line[4]))<<4; break; // Segmentvorgabe
case 4: ha=DWORD(MAKEWORD(line[5],line[4]))<<16; break; // High-Adressvorgabe
}
}
}
static int readHexFile(BYTE checkChpCfg) {
HANDLE h=CreateFile(HexFileName,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
if (h==INVALID_HANDLE_VALUE) return -6;
DWORD size=GetFileSize(h,0);
HANDLE hm=CreateFileMapping(h,0,PAGE_READONLY,0,0,0);
if (!hm) return -6;
const void*p=MapViewOfFile(hm,FILE_MAP_READ,0,0,0);
get gp(p,size); // Get-Pointer (2 Zeiger) basteln
int r=ParseHexFile(gp,checkChpCfg);
UnmapViewOfFile(p);
CloseHandle(hm);
CloseHandle(h);
return r;
}
/***********************
* Flash programmieren *
***********************/
namespace pubio{
static int ProgSector(WORD flashaddr, const BYTE*mem) {
WORD ff=DefFlash;
BYTE cs=0,e=flashaddr==0x8000?8:64;
for (BYTE b=0; b<e; b++) {
cs+=mem[b]; // Neu 210618: Prüfsumme nur über IDLOC
((BYTE*)&ff)[b&1]&=mem[b];
}
struct{
WORD addr; // Programmspeicher-Wortadresse (Little-Endian)
BYTE cs; // Datenblock muss nach Summierung mit cs 0 ergeben
char erase; // Lösch-Flag (auch bei IDLOC erforderlich?)
}block={flashaddr,256-cs,/*flashaddr==0x8000 ? 'P' :*/'E'}; // HACK!!
if (flashaddr<512) return 17;
int r=We(&block,4); if (r!=1) return r; // sonstiges r = Erase-Fehler
if (ff==DefFlash) return 1; // nichts zu tun (leerer Flash)
return We(mem,64); // sonstiges r = Verify-Fehler
}
static int Prog() {
// Teil 1: Hex-Datei einlesen (TODO: ELF-Datei einlesen; Makefile ausführen)
if (config.progflags&1) {
int r=readHexFile(config.progflags<<4);
if (r<0) return r;
}
// Teil 2: Daten zum Flash übertragen
if (!OpenComm()) return -7;
for (int a=1024; a<sizeof HexData; a+=64) {
int r=ProgSector(a>>1,HexData+a);
if (r!=1) return r;
}
if (haveID) {
int r=ProgSector(0x8000,IdSpace);
if (r!=1) return r;
}
if (config.progflags&0x80) return JumpToApp();
return 1;
}
}//namespace pubio
/*****************************
* Intel-Hex-Datei schreiben *
*****************************/
const BYTE maxline=32; // max. 16 Bytes pro Zeile
static void writeHexLine(HANDLE h, const BYTE*data, BYTE len) {
char line[1+(4+maxline+1)*2+3],*p=line;
*p++=':';
BYTE cs=0;
do{
BYTE b=*data++;
cs-=b;
p+=wsprintfA(p,"%02X",b);
}while(--len);
p+=wsprintfA(p,"%02X\r\n",cs);
DWORD bw;
WriteFile(h,line,DWORD(p-line),&bw,0);
}
// len darf nicht größer als maxline sein!!
static void writeHexLine(HANDLE h, BYTE type, BYTE len=0, const BYTE*data=0, WORD addr=0) {
BYTE dat2[4+maxline];
dat2[0]=len;
dat2[1]=HIBYTE(addr);
dat2[2]=LOBYTE(addr);
dat2[3]=type;
memcpy(dat2+4,data,len);
writeHexLine(h,dat2,len+4);
}
// Letzte genutzte Flash-Adresse eines Speicherbereiches ermitteln; len muss gerade sein!
// Liefert Byte-Index der ersten ungenutzten Flash-Adresse, immer gerade
static unsigned lastusedflash(const BYTE*data,unsigned len) {
unsigned r=len>>1;
while (r && (((WORD*)data)[r-1]&DefFlash)==DefFlash) --r;
return r<<1; // gerade Byteadresse, 0 wenn alles gelöscht ist
}
// Der PIC16F145x liest die nichtvorhandenen Flash-Bits als 0.
// Für bessere Übersichtlichkeit der HEX-Datei mag es besser sein,
// diese Bits auf 1 zu setzen:
// Leerer Flash erscheint dann nicht als 0x03FF sondern als 0xFFFF.
// Leider reicht das nicht zum Vergleich mit der HEX-Datei,
// die von picasm(?) / sdcc(?) generiert wird:
// Dort werden diese beiden Bits nach derzeit unbekanntem Muster belegt,
// vermutlich opcode-abhängig.
static void SetFlashHiBits(BYTE*data,unsigned len) {
for (;len;len-=2,data+=2) *(WORD*)data|=~DefFlash;
}
// Eine Zeile Hex-Daten (ID=0) mit Adresse <addr> ausgeben.
// Wenn Bit 5 in config.saveflags gelöscht:
// Zeile auf tatsächlich genutzte Flash-Words (von hinten) kürzen.
// Enthält diese Zeile keine genutzen Flash-Words, nichts tun, Zeile weglassen.
static void writeHexData(HANDLE h, WORD addr, BYTE len, const BYTE*data) {
// Erforderliche Länge feststellen; Freispeicher nicht ausspucken
if (!(config.saveflags&32)) len=lastusedflash(data,len);
if (!len) return;
writeHexLine(h,0,len,data,addr);
}
static void writeHexData(HANDLE h, BYTE linelen, const BYTE*data, unsigned a, unsigned e) {
for (unsigned i=a; i<e; i+=linelen) {
if (linelen>e-i) linelen=e-i;
writeHexData(h,(WORD)i,linelen,data+i);
}
}
static void writeHexFile(HANDLE h) {
if (config.saveflags&0x40) { // so wie mpasm(?) das liefert
SetFlashHiBits(HexData,sizeof HexData); // Gelesene Flash-Daten vorverarbeiten: verändern
SetFlashHiBits(IdSpace,sizeof IdSpace);
}// (war wohl bei älteren PICs so, dass 0xFFFF statt 0x3FFF (14 bit) bzw. 0xFFF (12 bit) gelesen wurde.)
unsigned a=0,e=sizeof HexData;
if (!(config.saveflags&1)) a=0x400; // Ohne Urlader
if (!(config.saveflags&2)) e=lastusedflash(HexData,e); // Nicht der ganze Flash
writeHexData(h,config.saveflags&0x80?32:16,HexData,a,e);
if (config.saveflags&0x1C) {
const BYTE extendedaddress[2]={0,1};
writeHexLine(h,4,2,extendedaddress);
if (config.saveflags&0x04) writeHexData(h,0,8,IdSpace); // User-ID
if (config.saveflags&0x08) writeHexData(h,10,4,IdSpace+10); // Chip-ID sowie Revision
if (config.saveflags&0x10) writeHexData(h,14,4,IdSpace+14); // Konfiguration
}
writeHexLine(h,1); // EOF
}
/***************
* Flash lesen *
***************/
// Zum Beschreiben von IDLOC muss ein Sektor gelesen werden,
// sonst klappt das Verify in der (knappen) Firmware nicht.
// Aber da lauert noch ein Firmware-Fehler (2021):
// Das Programmieren der IDLOC-Worte geht nicht!
static int pubioReadSector(WORD flashaddr, BYTE*mem) {
if (!OpenComm()) return -7;
struct{
WORD addr;
BYTE cs;
char erase;
}block={flashaddr,0,'R'};
if (WriteComm(&block,4)!=4) {CloseComm(); return -3;}
if (ReadComm(mem,64)!=64) {CloseComm(); return -18;}
return 1;
}
static int pubioReadAll() {
for (int a=0; a<sizeof HexData; a+=64) { // Lesen mit Urlader (kann man nachher leicht herauslöschen)
int r=pubioReadSector(a>>1,HexData+a);
if (r!=1) return r;
}
return pubioReadSector(0x8000,IdSpace); // sollte überflüssig sein, ist bereits gelesen
}
namespace pubio{
static int ReadFlash() {
// Teil 1: Flash lesen (der ID-Sektor wurde bereits gelesen)
if (!OpenComm()) return -7;
int r=pubioReadAll();
if (r!=1) return r;
// Teil 2: Hex-Datei schreiben (TODO: ELF-Datei)
TCHAR f[64]; // Filter-String mit Nullen aus Ressource
f[LoadString(0,33,f,elemof(f)-1)+1]=0; // mit Doppel-Null terminieren
TCHAR n[260];
lstrcpyn(n,HexFileName,elemof(n));
OPENFILENAME ofn;
ZeroMemory(&ofn,sizeof ofn);
ofn.lStructSize=sizeof ofn;
ofn.hwndOwner=MainWnd;
ofn.lpstrFile=n;
ofn.nMaxFile=elemof(n);
ofn.lpstrFilter=f;
ofn.nFilterIndex=1;
ofn.Flags=OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT|OFN_HIDEREADONLY;
ofn.lpstrDefExt=f+lstrlen(f)+3; // übergehe "\0*." und zeige auf "hex\0"
if (!GetSaveFileName(&ofn)) return 1; // Keinen weiteren Fehler anzeigen lassen
HANDLE h=CreateFile(n,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);
if (h==INVALID_HANDLE_VALUE) return -10;
writeHexFile(h);
CloseHandle(h);
return 1;
}
static int CompSector(WORD flashaddr, const BYTE*mem) {
if (!OpenComm()) return -7;
if (flashaddr==0x8000) {
if (!(config.veriflags&0x70)) return 64; // Kein Extra-Vergleich
}else{
if (!(config.veriflags&0x04)) return 64; // Kein Flash-Vergleich
}
struct{
WORD addr;
BYTE cs;
char erase;
}block={flashaddr,0,'R'};
if (WriteComm(&block,4)!=4) {CloseComm(); return -3;}
BYTE sec[64];
if (ReadComm(sec,64)!=64) {CloseComm(); return -18;}
const WORD*s=reinterpret_cast<const WORD*>(sec),
*m=reinterpret_cast<const WORD*>(mem);
for (unsigned i=0; i<32; i++,s++,m++) {
if (flashaddr==0x8000) {
// Beim Vergleich die IdSpace-Wortadressen 0x8004 (undef.) und 0x8005 (Revisions-ID) nicht mit einbeziehen!
// Hingegen User-IDs (0x8000..0x8003), Chip-ID (0x8006) und Konfigurationswörter (0x8007 + 0x8008) werden verglichen.
if ( i<4 && !(config.veriflags&0x10)) continue;
if (4<=i && i<6) continue;
if (i==6 && !(config.veriflags&0x20)) continue;
if (7<=i && i<9 && !(config.veriflags&0x40)) continue;
if (9<=i) continue;
}else{
if (!(config.veriflags&8) && !(~*m&DefFlash)) continue;
}
if (*m!=*s) return i<<1;
}
return 64;
}
// Beim Vergleich werden die beiden Hi-Bits ausgenommen.
// picasm / sdcc scheinen diese Bits wahlfrei zu benutzen (je nach Opcode?)
// werden nicht auf einen festen Wert gelegt.
static int CompAll() {
int r,a=config.veriflags&2?0:1024; // Vergleich mit/ohne Urlader
if (config.veriflags&4) for (; a<sizeof HexData; a+=64) {
r=CompSector(a>>1,HexData+a);
if (r!=64) return r<0?r:a+r;
}
a=0x10000;
r=CompSector(a>>1,IdSpace);
if (r!=64) return r<0?r:a+r;
return -1; // hier: OK!!
}
static int Verify() {
if (config.veriflags&1) {
int r=readHexFile(config.veriflags);
if (r<0) return r; // hier kommt kein -1
}
return CompAll(); // hier ist -1 = okay, ab 0 = fehlerhafte Flash-Adresse
}
}//namespace pubio
/*******
* GUI *
*******/
static void _cdecl showError(int r, ...) {
if (r==1) return;
if (r<0) r=-r;
UINT k=MB_OK;
if (r) k|=MB_ICONEXCLAMATION;
va_list va;
va_start(va,r);
vMBox(r,k,va);
va_end(va);
}
static void FillComboComPorts() {
HWND hCombo=GetDlgItem(MainWnd,101);
// serielle Schnittstellen (neu) listen (bei jedem WM_DEVICECHANGE bspw. für USB)
ComboBox_ResetContent(hCombo);
HANDLE devs=SetupDiGetClassDevs((LPGUID)&GUID_DEVCLASS_PORTS,0,0,DIGCF_PRESENT);
if (devs!=INVALID_HANDLE_VALUE) {
SP_DEVINFO_DATA devInfo;
devInfo.cbSize=sizeof devInfo;
for (DWORD i=0; SetupDiEnumDeviceInfo(devs,i,&devInfo); i++) {
HKEY hKey;
TCHAR s[16]; // trotzdem ein Unicode-String
*s=0;
if ((hKey=SetupDiOpenDevRegKey(devs,&devInfo,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ))
==INVALID_HANDLE_VALUE) continue;
DWORD len=sizeof(s);
RegQueryValueEx(hKey,T("PortName"),0,0,(LPBYTE)s,&len);
RegCloseKey(hKey);
if (*s=='C') { // Fehlschläge und LPTx ausfiltern
int idx=ComboBox_AddString(hCombo,PTSTR(s));
DWORD num=StrToInt(s+3)-1; // nullbasierte Nummer
ComboBox_SetItemData(hCombo,idx,num);
if (num==config.ComNr) ComboBox_SetCurSel(hCombo,idx);
}
}
SetupDiDestroyDeviceInfoList(devs);
}
}
static void NotifyParent(HWND hWnd,WORD cbn) {
SendMessage(GetParent(hWnd),WM_COMMAND,MAKELONG(GetDlgCtrlID(hWnd),cbn),(LPARAM)hWnd);
}
static void ShowAddress(HWND hCombo) {
TCHAR s[12];
TCHAR*sp=s+wnsprintf(s,elemof(s)-3,T("0x%03X"),config.addr);
BYTE f=config.flags;
if (f&0x08) *sp++='v'; // veränderlich beim Lesen
if (f&0x10) *sp++='W'; // Big Endian Word
else if (f&0x20) *sp++='w'; // Little Endian Word
*sp=0;
SetWindowText(hCombo,s);
NotifyParent(hCombo,CBN_EDITCHANGE);
}
static TCHAR*append(TCHAR*d,TCHAR const*e,char c) {
if (d!=e) *d++=c;
return d;
}
static TCHAR*append(TCHAR*d,TCHAR const*e,char const*s,int l=-1) {
if (s) {
#ifdef UNICODE
d+=MultiByteToWideChar(CP_ACP,0,s,l,d,int(e-d));
#else
if (l<0) l=lstrlenA(s);
if (l>e-d) l=(e-d);
memcpy(d,s,l); d+=l;
#endif
}
return d;
}
static void FillComboSymbols() {
HWND hCombo=GetDlgItem(MainWnd,102);
ComboBox_ResetContent(hCombo);
const char*p=pic16f1459sym;
while (*p) {
const char*q=p;
while (*q>=0x20) ++q; // Ende des Namens finden
TCHAR s[32];
*append(s,s+elemof(s)-1,p,int(q-p))=0;
if (!detectedChip
|| detectedChip==4 && q[2]&0x01
|| detectedChip==5 && q[2]&0x02
|| detectedChip==9 && q[2]&0x04) { // Symbol-Liste passend zum aktuellen Chip
int idx=ComboBox_AddString(hCombo,s);
DWORD a=MAKELONG(MAKEWORD(q[1],q[0]),q[2]);
ComboBox_SetItemData(hCombo,idx,a);
if (LOWORD(a)==config.addr // Adresse gleich?
&& !((q[2]^config.flags)&0xB0)) { // Byte/Word gleich? Adresse nicht als Edit?
ComboBox_SetCurSel(hCombo,idx);
NotifyParent(hCombo,CBN_SELCHANGE);
}
}
p=q+3;
}
if (ComboBox_GetCurSel(hCombo)<0) ShowAddress(hCombo);
}
static void DuplicateCheckboxes() {
int i=23;
HWND cb=GetDlgItem(MainWnd,i); // Erste Checkbox aus der Ressource, gibt Maße vor
HFONT f=GetWindowFont(MainWnd);
WINDOWINFO wi;
wi.cbSize=sizeof wi;
GetWindowInfo(cb,&wi);
wi.dwStyle&=~WS_GROUP;
wi.dwStyle|= WS_VISIBLE;
wi.dwExStyle&=0x0000FFFF; // Windows 10: Manchmal kommt 0xC0000804 statt 0x00000804
// und dann geht CreateWindow() schief!
wi.rcWindow.right-=wi.rcWindow.left; // Breite
wi.rcWindow.bottom-=wi.rcWindow.top; // Höhe
ScreenToClient(MainWnd,(PPOINT)&wi.rcWindow);
while (--i>=16) {
wi.rcWindow.left+=wi.rcWindow.right; // rechts daneben erzeugen
HWND w=CreateWindowEx(wi.dwExStyle,MAKEINTRESOURCE(wi.atomWindowType),0,wi.dwStyle,
wi.rcWindow.left,wi.rcWindow.top,wi.rcWindow.right,wi.rcWindow.bottom,
MainWnd,(HMENU)i,0,0);
SetWindowFont(w,f,TRUE);
SetWindowPos(w,cb,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); // In Tab-Reihenfolge setzen
cb=w;
}
}
static char const*endofbitname(char const*n) {
while ((BYTE)*n>=8) n++;
return n;
}
static void SetBitName(HWND w,char const*p,char const*e,char bit=-1) {
TCHAR S[16],*s;
s=append(S,S+elemof(S)-1,p,int(e-p));
if (bit>=0) s=append(s,S+elemof(S)-1,bit+'0'); // Ziffer
*s=0;
SetWindowText(w,S);
EnableWindow(w,p!=e); // p==e bedeutet: Disabled
}
static void SetBitNames(BYTE mask,char const*names=0) {
char const*p=names,*e=names-1;
byte bitcount=p?0:8,bit=0,n=0;
for (HWND w=GetDlgItem(MainWnd,16);n<8;w=GetPrevSibling(w),--bitcount,++bit,mask>>=1,++n) {
if (bitcount) SetBitName(w,p,mask&1?e:p,e!=p?bit:n);
else{
p=e+1;
e=endofbitname(p);
bit=0;
bitcount=*e; if (!bitcount) bitcount=1; // Anzahl Bits
SetBitName(w,p,mask&1?e:p,p!=e?bitcount<2?-1:bit:n);
}
}
assert(!bitcount);
}
static void SetBitNames() {
SendDlgItemMessage(MainWnd,84,UDM_SETRANGE32,0,config.datamax());
for (int i=16; i<24; i++) ShowWindow(GetDlgItem(MainWnd,i),config.flags&0x30?SW_HIDE:SW_SHOWNA);
if (config.flags&0x30) return; // Keine Bits bei 16-Bit-Registern anzeigen
// BDT-Adressen: Nur BDnSTAT, n=0..31
// 210620: Bitnamen für BDnSTAT haben sich als verwirrend herausgestellt, doch wieder weg damit
// if (!(config.bdt()&0xFF83)) { // 0,4,8..124
// if (false) SetBitNames(0xBF,"BC8\1BC9\1PID\4\1UOWN"); //UOWN=1: SIE kontrolliert BDnSTAT
// else SetBitNames(0xCF,"BC8\1BC9\1BSTALL\1DTSEN\1\2DTS\1UOWN");//UOWN=0: CPU kontrolliert BDnSTAT
// return;
// }
char const*p=pic16f1459bits;
WORD a=MAKEWORD(p[1],p[0]); p+=2;
for(;;){
if (a==config.addr) {
if (*p==-1) SetBitNames(p[1]);
else SetBitNames(0xFF,p);
return;
}else{
if (*p==-1) p+=2; // Maske
else{
int i=8; // 8 Bitdefinitionen, jeweils mit \0 ..\7 abgeschlossen
do{
p=endofbitname(p);
byte c=*p++;
if (!c) c=1;
assert(c<=i);
i-=c;
}while(i);
}
}
a=MAKEWORD(p[1],p[0]); p+=2;
if (!a) {
SetBitNames(0xFF,0);
return;
}
}
}
// Adresse von config.Addr und config.flags (16 bit) interpretieren
// Bit 7 von config.flags: Benutzer hat Adresse manuell eingegeben: SFR dekodieren
static void OutAddressInfo() {
TCHAR S[64],*s=S,*e=S+elemof(S)-1;
WORD a=config.addr;
BYTE f=config.flags;
if (f&0x80) {
// Symbolanzeige zur Adresse realisieren, dabei Hi- und Lo-Bytes auflösen
WORD m=a&0xF07F;
if (m<0x0C || 0x70<=m && m<0x80) a&=0x7F; // Gemeinsame Register auffinden lassen
f&=0x30;
for (const char*q,*p=pic16f1459sym; *(q=p); p=q+3) {
while (*q>=0x20) ++q; // Ende des Namens finden
WORD A=MAKEWORD(q[1],q[0]);
BYTE F=q[2];
if (!detectedChip
|| detectedChip==4 && F&0x01
|| detectedChip==5 && F&0x02
|| detectedChip==9 && F&0x04) { // passend zum aktuellen Chip
char lh=-1; // Ergebnis der Symbolsuche: "", "L" oder "H" wenn Treffer
F&=0x30; // Nur 8/16 bit
if (!f && F) { // Byte-Adresse gegeben und 16-Bit-Symbol?
if (A==a) lh=F&0x10?'H':'L'; // Big-Endian: Hi auf niederer Adresse
if (A==a-1) lh=F&0x20?'H':'L'; // Little-Endian: Hi auf höherer Adresse
}else if (f==F && A==a) lh=0; // Gleiche Adresse
if (lh>=0) {
s=append(s,e,p,int(q-p));
if (lh) s=append(s,e,lh);
s=append(s,e,'\n');
break;
}
}
}
}
if (a>=0x8000) {
TCHAR t[64];
LoadString(0,42,t,elemof(t)); // "Flash ROM"
s+=wnsprintf(s,int(e-s),T("%s 0x%04X\n"),t,a&0x1FFF); // Lineare Adresse
}else{
BYTE b,ba; // Bank (0..31 oder 255=any) und Bank-Adresse (0..7F)
if (a<0x1000) { // Bank-Adresse in Lineare Adresse umrechnen (wenn möglich)
b=a>>7; ba=a&0x7F;
if (ba<0x0C || ba>=0x70) b=255; // In allen Bänken
a=config.linaddr(); // 0 = keine lineare Adresse
}else{ // Lineare Adresse in Bank-Adresse umrechnen (wenn möglich)
WORD aa=a-0x2000;
b=aa/80; ba=aa%80+0x20; // PIC-interne Umrechnungsformel
if (b>=31) ba=0xFF; // jenseits Bank 30 nicht für PIC adressierbar: 31×80+16=2496=0x9C0
}
if (a) {
if (!(a-0x2000&0xFF80)) { // Buffer Descriptor Table (Länge sicherlich kürzer)
const char*t=0,*lh="";
switch (a&3) {
case 0: if (!f) t="STAT"; break;
case 1: if (!f) t="BC"; break;
case 2: if (!(f&~0x20)) t="ADR"; if (!f) lh="L"; break;
case 3: if (!f) t="ADR"; lh="H"; break;
}
if (t) s+=wnsprintf(s,int(e-s),T("BD%d%hs%hs\n"),a>>2&0x1F,t,lh);
} // "%hs" gibt 8-Bit-String aus, egal ob wnsprintfA oder wnsprintfW
TCHAR t[64];
BYTE id=38; // "Undefined";
if (ba!=0xFF) {
if (a>=0x23F0) id=41; // "Unimplemented"
else if (a>=0x21F0) id=40; // "Single-Port RAM"
else if (a>=0x2000) id=39; // "Dual-Port RAM"
}
LoadString(0,id,t,elemof(t));
s+=wnsprintf(s,int(e-s),T("%s 0x%04X\n"),t,a); // Lineare Adresse
}
if (ba!=255) { // Bank-Adresse vorhanden? (Lineare Adresse zwischen 0x2000 und 0x29BF)
TCHAR t[64];
LoadString(0,36+(b==255),t,elemof(t)); // "Bank %d Addr. %X"
if (b==255) s+=wnsprintf(s,int(e-s),t,ba);
else s+=wnsprintf(s,int(e-s),t,b,ba);
}
}
SetDlgItemText(MainWnd,107,S);
}
static void EnableRead() {
bool f=!!(config.flags&0x08);
EnableWindow(GetDlgItem(MainWnd,4),f);
if (f) KillTimer(MainWnd,3);
else SetTimer(MainWnd,3,config.read_time*10,0);
SetDlgItemText(MainWnd,103,0); // Gelesenes Byte nicht mehr anzeigen
}
static INT_PTR CALLBACK TimeDlgProc(HWND Wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_INITDIALOG: {
SetDlgItemInt(Wnd,11,config.read_time*10,FALSE);
}return TRUE;
case WM_COMMAND: switch (wParam) {
case IDOK: {
UINT v=GetDlgItemInt(Wnd,11,NULL,FALSE);
if (v && v<=1000) config.read_time=(v+9)/10;
else{
HWND w=GetDlgItem(Wnd,11);
Edit_SetSel(w,0,-1);
SetFocus(w);
MessageBeep(MB_ICONHAND);
break;
}
}/*nobreak*/
case IDCANCEL: EndDialog(Wnd,wParam); break;
}break;
}
return FALSE;
}
static void HandleMenu(int menupos) {
if (menupos<0) menupos=3; // Extrawurst für Lesen-Knopf
DWORD pos=GetMessagePos();
HMENU hm=LoadMenu(0,MAKEINTRESOURCE(2));
HMENU sm=GetSubMenu(hm,menupos);
BYTE&b=(&config.saveflags)[menupos];
if (menupos==3) {
CheckMenuItem(sm,0,config.flags&8?MF_UNCHECKED|MF_BYPOSITION:MF_CHECKED|MF_BYPOSITION);
EnableMenuItem(sm,1,config.flags&8?MF_DISABLED|MF_BYPOSITION:MF_ENABLED|MF_BYPOSITION);
}else{
for (BYTE m=1,i=0;m;m<<=1,i++) CheckMenuItem(sm,i,
b&m?MF_CHECKED|MF_BYPOSITION:MF_UNCHECKED|MF_BYPOSITION);
}
int e=TrackPopupMenu(sm, // alle IDs sind hier fortlaufend 16..23
TPM_LEFTALIGN|TPM_TOPALIGN|TPM_RETURNCMD,
GET_X_LPARAM(pos),GET_Y_LPARAM(pos),0,MainWnd,0);
if (e>=16) {
if (menupos==3) switch (e){
case 0: config.flags^=8; break; // TODO: Aussehen anpassen!
case 1: DialogBox(0,MAKEINTRESOURCE(2),MainWnd,TimeDlgProc); break; // Mini-Dialog
}else b^=1<<(e-16); // Bit toggeln
}
DestroyMenu(hm);
}
static WNDPROC DefButtonProc;
static LRESULT CALLBACK MyButtonWndProc(HWND Wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CONTEXTMENU: HandleMenu(GetWindowID(Wnd)-8); break;
}
return CallWindowProc(DefButtonProc,Wnd,msg,wParam,lParam);
}
static void MakeSplitButton(HWND w) {
if (LOBYTE(GetVersion())>=6) { // Knopf zum SplitButton machen
// Unter Windows XP würde das zum Verschwinden des Knopfes führen.
// Fraglich was passiert wenn comctlXX.dll nicht geladen wird.
SetWindowLong(w,GWL_STYLE,GetWindowStyle(w)|12);//BS_SPLITBUTTON
} //WM_PARENTNOTIFY ist zu umständlich
DefButtonProc=SubclassWindow(w,MyButtonWndProc);
}
static INT_PTR CALLBACK MainDlgProc(HWND Wnd,UINT msg,WPARAM wParam,LPARAM lParam) {
switch (msg) {
case WM_INITDIALOG: {
MainWnd=Wnd; // für CloseComm (bei Fehler u.ä.)
config.saveflags=0x3C; // standardmßig alle IDs und ohne Flash-Lücken bei 0x3FFF
config.progflags=0xFF; // standardmäßig alle Aktionen
config.veriflags=0x7D; // standardmäßig alles außer Urlader vergleichen
config.read_time=10; // 100 ms
MakeSplitButton(GetDlgItem(Wnd,8)); // auslesen (Submenü der zu speichernden Items usw.)
MakeSplitButton(GetDlgItem(Wnd,9)); // programmieren (Submenü der Aktionen)
MakeSplitButton(GetDlgItem(Wnd,10)); // vergleichen (Submenü was zu vergleichen ist)
// MakeSplitButton(GetDlgItem(Wnd,4)); // lesen (Millisekunden)
LoadSettings();
WINDOWPLACEMENT wp;
wp.length=sizeof wp;
GetWindowPlacement(Wnd,&wp);
OffsetRect(&wp.rcNormalPosition,
config.posX-wp.rcNormalPosition.left,
config.posY-wp.rcNormalPosition.top);
SetWindowPlacement(Wnd,&wp);
SetClassLongPtr(Wnd,GCLP_HICON,(LONG_PTR)LoadIcon(GetModuleHandle(0),MAKEINTRESOURCE(1)));
GetWindowText(Wnd,MBoxTitle,elemof(MBoxTitle));
DuplicateCheckboxes();
FillComboComPorts();
HWND hCombo=GetDlgItem(Wnd,102);
HWND hUpDn=CreateUpDownControl(WS_VISIBLE|WS_CHILD
|UDS_WRAP|/*UDS_SETBUDDYINT|*/UDS_ALIGNRIGHT/*|UDS_NOTHOUSANDS*/,
0,0,0,0,Wnd,82,0,hCombo,0xFFF,0,config.addr);
FillComboSymbols(); // Symbole für Adresse
SendMessage(hUpDn,UDM_SETBASE,16,0);
HWND hEdit=GetDlgItem(Wnd,104); // UpDown für Datenbyte anhängen
hUpDn=CreateUpDownControl(WS_VISIBLE|WS_CHILD
|UDS_WRAP|UDS_SETBUDDYINT|UDS_ALIGNRIGHT|UDS_ARROWKEYS|UDS_NOTHOUSANDS,
0,0,0,0,Wnd,84,0,hEdit,config.datamax(),0,config.data);
// SetWindowPos(hUpDn,hEdit,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_DEFERERASE);
SendMessage(hUpDn,UDM_SETBASE,16,0);
#ifdef UNICODE
int argc;
PTSTR*argv=CommandLineToArgvW(GetCommandLine(),&argc);
if (argc>=2) {
lstrcpyn(HexFileName,argv[1],elemof(HexFileName));
PostMessage(Wnd,WM_COMMAND,9,0); // Knopf "Programmieren" drücken
PostMessage(Wnd,WM_COMMAND,IDCANCEL,0); // Knopf "Ende" drücken
}
GlobalFree(argv);
#endif
SetDlgItemText(Wnd,105,HexFileName);
SetTimer(Wnd,2,500,0);
}return TRUE;
case WM_DEVICECHANGE: CloseComm(); SetTimer(Wnd,1,2000,0); break;
case WM_TIMER: switch (wParam) {
case 1: { // Wartezeit nach WM_DEVICECHANGE abgelaufen: COM-Schnittstellen neu listen
KillTimer(Wnd,wParam);
FillComboComPorts(); // Erneut füllen
SetTimer(Wnd,2,200,0);
}break;
case 2: { // Wartezeit nach Schnittstellen-Auswahl abgelaufen: PIC suchen
KillTimer(Wnd,wParam);
__stosw(IdSpaceW,DefFlash,sizeof IdSpace>>1);
int r=pubio::Read(0x8200);
if (r>=0) {
EnableRead();
EnableDlgItem(Wnd,5,true);
for (UINT i=8; i<11; i++) EnableDlgItem(Wnd,i,true);
if (r!=0xFF) EnableDlgItem(Wnd,11,true);
}
if (pubioReadSector(0x8000,IdSpace)==1) {
WORD id=IdSpaceW[6];
TCHAR s[64],t[64],p[16];
byte z=0, lowpower=id&4;
if (HIBYTE(id)==0x30) switch (LOBYTE(id)&0xFB) {
case 0x20: z=4; break; // PIC16F1454
case 0x21: z=5; break; // PIC16F1455
case 0x22: z=8; break; // PIC16F1458 (obsolet)
case 0x23: z=9; break; // PIC16F1459
}
if (z) {
detectedChip=z;
wnsprintf(p,elemof(p),T("PIC16%sF145%u"),lowpower?T("L"):T(""),z);
FillComboSymbols(); // Liste ändern/einschränken
}else{
LoadString(0,35,s,elemof(s)); // "unbekannt, ID=%X"
wnsprintf(p,elemof(p),s,id);
}
LoadString(0,34,t,elemof(t));
wnsprintf(s,elemof(s),t,p);
SetDlgItemText(Wnd,106,s);
}
}break;
case 3: { // Automatisches Lesen (alle 100 ms, einstellbar)
if (hCom) SendMessage(Wnd,WM_COMMAND,4,0); // Knopf „Lesen“ drücken
else KillTimer(Wnd,wParam);
}break;
}break;
case WM_COMMAND: switch (wParam) {
case MAKELONG(101,CBN_SELCHANGE): {
CloseComm();
config.ComNr=(BYTE)ComboBox_GetItemData((HWND)lParam,ComboBox_GetCurSel((HWND)lParam));
SetTimer(Wnd,2,200,0); // Erkennung starten
}break;
case MAKELONG(102,CBN_EDITCHANGE): {
KillTimer(Wnd,3);
TCHAR s[64];
s[0]='0';
s[1]='x';
BYTE f=0; // Flags, Bit 3 = veränderlich beim Lesen, Bit 4 = 2 Byte Big Endian, Bit 5 = 2 Byte Little Endian
TCHAR*e=s+2+GetWindowText((HWND)lParam,s+2,elemof(s)-2);
while (e!=s) {
TCHAR c=*--e;
if (c=='w' && !(f&0x30)) {f|=0x20; *e=0; continue;} // Little-Endian-Suffix
if (c=='W' && !(f&0x30)) {f|=0x10; *e=0; continue;} // Big-Endian-Suffix
if (c=='v' && !(f&0x08)) {f|=0x08; *e=0; continue;} // Veränderlich-Suffix
break;
}
TCHAR*p=s;
if (s[2]=='0' && s[3]=='x') p+=2;
int a;
if (!StrToIntEx(p,STIF_SUPPORT_HEX,&a)) {
MessageBeep(0); // Bereichsüberlauf, falsche Zeichen u.ä.
s[0]=0;
}else{
config.addr=(WORD)a;
config.flags=f|0x80; // Bit 7 = 1: Als Zahl gegeben
OutAddressInfo();
SetBitNames();
EnableRead();
}
}break;
case MAKELONG(102,CBN_SELCHANGE): {
KillTimer(Wnd,3);
DWORD a=DWORD(ComboBox_GetItemData((HWND)lParam,ComboBox_GetCurSel((HWND)lParam)));
config.addr=LOWORD(a);
config.flags=LOBYTE(HIWORD(a)); // Bit 7 = 0: Als SFR gegeben
OutAddressInfo();
SetBitNames();
EnableRead(); // Knopf "Lesen" aktivieren wenn veränderlich beim Lesen
}break;
case MAKELONG(104,EN_CHANGE): {
TCHAR s[8]; // Weil das UpDownControl immer die Form „0x04AF“ ausgibt
GetWindowText((HWND)lParam,s,elemof(s));
if (s[0]=='0' && s[1]=='x') SetWindowText((HWND)lParam,s+(config.flags&0x30?2:4));
BOOL err;
WORD d=(WORD)SendDlgItemMessage(Wnd,84,UDM_GETPOS32,0,(LPARAM)&err);
if (err) MessageBeep(0); // Bereichsüberlauf, falsche Zeichen u.ä.
else config.data=d;
}break;
case 4: { // Lesen (automatisch wenn nicht beim Lesen veränderlich oder den Urlader beeinflussend)
int r=pubio::Read(config.addr);
if (r<0) {if (lParam) showError(r); break;}
PCTSTR t=T("%02X");
if (config.flags&0x30) { // 16 Bit?
t=T("%04X");
int rr=pubio::Read(config.addr+1);
if (rr<0) {if (lParam) showError(rr); break;}
r=config.flags&0x20 ? MAKEWORD(r,rr) : MAKEWORD(rr,r); // Big Endian gibt's nur bei 2 Registern: UFRM und TOS
}else{
for (int i=16,rr=r; i<24; i++,rr>>=1) CheckDlgButton(Wnd,i,rr&1); // Bits anzeigen
}
TCHAR s[6];
wnsprintf(s,elemof(s),t,r);
SetDlgItemText(Wnd,103,s); // Byte/Wort anzeigen
}break;
case MAKELONG(105,EN_CHANGE): {
GetWindowText((HWND)lParam,HexFileName,elemof(HexFileName));
}break;
case 5: { // Schreiben
int r=pubio::Write(config.addr,config.flags&0x10?HIBYTE(config.data):LOBYTE(config.data));
if (r==1 && config.flags&0x30)
r=pubio::Write(config.addr+1,config.flags&0x20?HIBYTE(config.data):LOBYTE(config.data));
showError(r);
}break;
case 6: {
MBox(32,MB_OK|MB_ICONINFORMATION);
}break;
case 7: { // Datei-Dialog
TCHAR f[64]; // Filter-String mit Nullen aus Ressource
f[LoadString(0,33,f,elemof(f)-1)+1]=0; // mit Doppel-Null terminieren
OPENFILENAME ofn;
ZeroMemory(&ofn,sizeof ofn);
ofn.lStructSize=sizeof ofn;
ofn.hwndOwner=Wnd;
ofn.lpstrFile=HexFileName;
ofn.nMaxFile=elemof(HexFileName);
ofn.lpstrFilter=f;
ofn.nFilterIndex=1;
ofn.Flags=OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
ofn.lpstrDefExt=f+lstrlen(f)+3; // übergehe "\0*." und zeige auf "hex\0"
if (GetOpenFileName(&ofn)) {
SetDlgItemText(Wnd,105,HexFileName);
}
}break;
case 9: { // Programmieren
showError(pubio::Prog(),get::line);
}break;
case 8: { // Lesen
showError(pubio::ReadFlash());
}break;
case 10: { // Vergleichen
int e=0,r=pubio::Verify();
if (r>=0) {e=25; r>>=1;} // Wortadresse
else if (r<-1) {e=-r; r=get::line;}
showError(e,r);
}break;
case 11: { // Äpp anspringen
pubio::JumpToApp();
}break;
case IDCANCEL: {
CloseComm();
WINDOWPLACEMENT wp;
wp.length=sizeof wp;
if (GetWindowPlacement(Wnd,&wp)) {
config.posX=(short)wp.rcNormalPosition.left;
config.posY=(short)wp.rcNormalPosition.top;
}
SaveSettings();
EndDialog(Wnd,wParam);
}break;
default: if (16<=wParam && wParam<24) { // Klick auf ein Bit
if (SendMessage((HWND)lParam,BM_GETCHECK,0,0)==BST_INDETERMINATE) {
byte mask=0x80;
for (HWND w=GetDlgItem(Wnd,23);mask;w=GetNextSibling(w),mask>>=1) {
SendMessage(w,BM_SETCHECK,0,0);
SetWindowLong(w,GWL_STYLE,GetWindowLong(w,GWL_STYLE)&~BS_3STATE|BS_AUTOCHECKBOX); // Ab nun wieder nur 2 Zustände
SendMessage(w,BM_SETCHECK,config.data&mask?1:0,0);
}
}else{
byte b;
for (int i=24;--i>=16;) b=b<<1|IsDlgButtonChecked(Wnd,i);
config.data=b;
SendDlgItemMessage(Wnd,84,UDM_SETPOS32,0,config.data);
SendMessage(Wnd,WM_COMMAND,5,0); // Schreiben auslösen
}
}
}break;
case WM_NOTIFY: {
NMHDR&hdr=*(NMHDR*)lParam;
switch (hdr.idFrom) {
case 82: switch (hdr.code) {
case UDN_DELTAPOS: {
NMUPDOWN&ud=*(NMUPDOWN*)lParam;
config.addr+=ud.iDelta;
ShowAddress(GetDlgItem(Wnd,102));
}break;
}break;
case 8: // Knopf „Lesen&Speichern“
case 9: // Knopf „Programmieren“
case 10: switch (hdr.code) {
case BCN_FIRST+2: HandleMenu(int(hdr.idFrom)-8); break;
}break;
}
}break;
}
return FALSE;
}
EXTERN_C void WinMainCRTStartup() {
InitCommonControls();
__stosw(HexDataW,DefFlash,sizeof HexData>>1);
__stosw(IdSpaceW,DefFlash,sizeof IdSpace>>1);
ExitProcess((UINT)DialogBox(0,MAKEINTRESOURCE(1),0,MainDlgProc));
}
Detected encoding: ANSI (CP1252) | 4
|
|