pkg://pp84-1.1.6b-2.src.rpm:201874/pp84-1.1.6b.tar.gz
info downloads
pp84-1.1.6b/ 40775 145 145 0 6573110463 11363 5 ustar nemeth nemeth pp84-1.1.6b/pic84.asm 100775 145 145 5572 6342110371 13115 0 ustar nemeth nemeth
; ********************************************
;
; PIC84.ASM
; PIC16C84 SAPMLE PROGRAM
; PIC 84 SOURCE CODE
;
; ********************************************
LIST F=INHX8M
PROCESSOR 16C84
RADIX DEC
__CONFIG B'11111' ; RC
; B4: CODE PROTECTION, 1=OFF
; B3: POWER UP, 1=TIMED
; B2: WDOG, 1=ON
; B1-0: OSC SELECT, 01=XT, 11=RC
__IDLOCS H'0001'
; ------------------------------------------------------
; EQUATES
; PORT B BIT ALLOCATIONS
BLED1 EQU 0 ; o/p ; bicolour led + => green
BLED2 EQU 1 ; o/p ; bicolour led + => red
; STORAGE ALLOCATIONS
; VARIABLE DATA STORAGE STARTS AT ADDR 12
TEMP1 EQU 12
; DATA STORAGE ENDS AT ADDR 47
; CONSTANTS
include p16c84.inc
; ---------------------------------------------------------
ORG 0
GOTO START
ORG 4
; interrupt service
RETFIE
; ---------------------------------------------------------
; SETUP
START MOVLW B'11111111' ; PORT REGS TO DEFAULTS
MOVWF PORTA
MOVLW B'11111111'
MOVWF PORTB
BSF STATUS,RP0 ; OPTION & TRIS ARE HI REGS
MOVLW B'00000111'
; B7: RBPU - ENABLE PORT B PULL-UP RES, 0=OFF
; B6: INTEDG - RB0 INT EDGE SEL 0=-VE
; B5: T0CS - TMR0 SOURCE, 0=FOSC/4, 1=RA4 PIN
; B4: T0SE - RA4CLK PIN EDGE SELECT, 0=+VE
; B3: PSA - PRESCALE ASSIGN 0=TIMER, 1=WDG
; B2-0: PS2-0 - PRESCALE RATE 111=256 FOR TIMER (=127 FOR WDT)
MOVWF OPTION_REG
MOVLW B'00000000' ; 0=OUTPUT
MOVWF TRISB
MOVLW B'11111' ; 1=INPUT
MOVWF TRISA
BCF STATUS,RP0 ; BACK TO LO REGS
MOVLW B'00000000'
; B7: GIE - GLOBAL INT ENABLE, 0=OFF
; B6: EEIE - EERAW WRITE DONE INT ENABLE, 0=OFF
; B5: T0IE - TMR0 O/FLOW INT ENABLE, 0=OFF
; B4: INTE - RB0 INT ENABLE, 0=OFF
; B3: RBIE - PORTB CHANGE-OF-STATE INT ENABLE, 0=OFF
; B2: T0IF - TMR0 O'FLOW FLAG, 1=O/FLOW, S/WARE MUST RESET
; B1: INTF - RB0 INT FLAG, ACTIVE HIGH
; B0: RBIF - PORTB CHANGE-OF-STATE FLAG, ACTIVE HIGH
MOVWF INTCON
; ----------------------------------------------------------------
MAIN:
; sample program flashes the b-colour LED
; from red to green once a second.
MOVLW 15
MOVWF TEMP1
LOOP0: BCF INTCON,T0IF
LOOP1: CLRWDT
BTFSS INTCON,T0IF
GOTO LOOP1
DECFSZ TEMP1
GOTO LOOP0
BSF PORTB,BLED1
NOP
BCF PORTB,BLED2
MOVLW 15
MOVWF TEMP1
LOOP2: BCF INTCON,T0IF
LOOP3: CLRWDT
BTFSS INTCON,T0IF
GOTO LOOP3
DECFSZ TEMP1
GOTO LOOP2
BCF PORTB,BLED1
NOP
BSF PORTB,BLED2
GOTO MAIN ; DONE
; ---------------------------------------------------------
ORG H'2100'
DE "EEProm data initialisation"
; --------------------------------------------------------
END
pp84-1.1.6b/pp84.doc 100775 145 145 2674 6544273175 12766 0 ustar nemeth nemeth PP84.DOC
PP84 programmer software general usage notes
Steve Marchant, CCC, Nottingham university
PP84 with no parameters will reset the in-system PIC
Other command line options via 'pp84 --help'
PP84 with the name of an 8-bit HEX file will read in the file,
validate it, report its 16 bit code check-sum, then proceed
to program the embedded data into the attached PIC16C84 device.
Data words at locations below $400 are programmed into code memory.
Data nibbles at locations $2000-2004 are programmed into ID memory.
Any Data item at location $2007 is programmed into the Config word.
Note that code protection is delayed 'til the end of programming.
Data bytes at locations $2100-$213F are programmed into EE data memory.
Programming errors are displayed to the screen, but programming continues.
Upto 5 errors are tollerated before aborting the job.
A code protected device will automatically be bulk erased.
Only the locations specified the HEX file are programmed & verified.
A location which already contains the correct data will not be programmed.
The software will not start unless a PROG84 adapter is connected, unless
forced by a command line option.
PP84 scans for PROG84 hardware at 3 common LPT port addresses.
An overall verify pass is executed after the programming sequence.
To Do List:
* Option to override OSC settings in config word.
Bugs:
Please report to: nemeth@mzperx.ddns.org (Linux)
pp84-1.1.6b/Makefile 100775 145 145 734 6446536511 13114 0 ustar nemeth nemeth SHELL = /bin/sh
srcdir = .
prefix = /usr/bin
exec_prefix = ${prefix}
libdir = $(exec_prefix)/lib
includedir = $(exec_prefix)/include
CC = gcc
CFLAGS = -O2 -Wall -fno-strength-reduce
CCFLAGS = $(CFLAGS)
LINK = $(CC)
LD_FLAGS = -lncurses
PROTO = pp84
.c.o:
$(CC) -c $(CCFLAGS) $<
all: $(PROTO)
pp84: $(srcdir)/pp84.c
$(LINK) $(CCFLAGS) $(srcdir)/$@.c $(LD_FLAGS) -o $@
install:
-install -oroot -groot -m4755 -s $(PROTO) $(prefix)
clean:
-rm -rf $(PROTO)
pp84-1.1.6b/pp84.c 100775 145 145 57034 6573013755 12461 0 ustar nemeth nemeth /* PIC16C84 programmer, using PROG84 hardware
* Copyright (C) 1996-1997 by Steve Marchant (TP code for DOS)
* Copyright (C) 1997-1998 by Laszlo Nemeth (Linux port)
*
* Original version by Steve Marchant
* (Steve.Marchant@Nottingham.ac.uk)
* Ported to Linux by Laszlo Nemeth
* (nemeth@mzperx.ddns.org)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <asm/io.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sched.h>
#include <curses.h>
#include <sys/timeb.h>
#include <sys/stat.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#ifdef __GLIBC__
#include <sys/io.h>
#endif
static const char *RELDATE = "01/09/98";
static const char *VERSION = "1.1.6b";
#define true 1
#define false 0
static const int lp1_add = 0x378;
static const int lp2_add = 0x278;
static const int lp0_add = 0x3bc;
static char keyw, cal;
static long cp, conf;
static short GPCounter;
static unsigned short LPTDAT, LPTSTAT;
static unsigned long DlyCal;
static int fd0 = -1, fd1 = -1, fd2 = -1;
static WINDOW *mainw;
static char lp0_use = false, lp1_use = false, lp2_use = false;
static char port_force = false;
static char hw_timer = false;
static char *
strsub(char *s, char *ct, int p, size_t n)
{
*s = '\0';
s = strncat(s, ct + p - 1, n);
return (s);
}
static void done()
{
if (fd0 >= 0)
close(fd0);
if (fd1 >= 0)
close(fd1);
if (fd2 >= 0)
close(fd2);
if (keyw) {
wprintw(mainw, "\nPress any key to finish\n");
wgetch(mainw);
}
endwin();
ioperm(LPTDAT, 8, 0);
ioperm(LPTSTAT, 8, 0);
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
exit(0);
}
static void calm()
{
cal = false;
}
static unsigned long dlyrecal(void)
{
unsigned long ct = 0;
double a;
FILE *dummy;
signal(SIGALRM, calm);
dummy = fopen("/dev/null", "w");
cal = true;
alarm(1);
while (cal == true) {
ct++;
fprintf(dummy, " ");
}
a = ct / 130;
fclose(dummy);
signal(SIGALRM, SIG_DFL);
return (a);
}
static void Dly(unsigned short del)
{
unsigned long ct;
if (del == 0)
del = 1;
del = (del * 10);
for (ct = 0; ct < (DlyCal * del); ct++)
ct = ct;
}
static void TickDly()
{
unsigned long ct, del;
del = DlyCal / 200;
if (del == 0)
del = 1;
for (ct = 0; ct < del; ct++)
ct = ct;
}
static long HexValue(char *s)
{
unsigned int i;
short j;
j = (sscanf(s, "%x", &i) == 0);
if (j == 0)
return i;
else
return -1;
}
static char Hex(short x)
{
char temp;
if (x < 10)
temp = x + '0';
else
temp = x - 10 + 'A';
if (x >= 16)
temp = 'X';
return temp;
}
static char *
HexStr2(char *Result, short x)
{
snprintf(Result, 3, "%c%c", Hex(x / 16), Hex(x & 15));
return Result;
}
static char *
HexStr4(char *Result, long x)
{
short i;
char tmp[20];
char STR1[20];
*tmp = '\0';
for (i = 1; i <= 4; i++) {
snprintf(tmp, sizeof(tmp), "%c%s", Hex((int) (x & 15)), strcpy(STR1, tmp));
x /= 16;
}
return strcpy(Result, tmp);
}
static char GetCodeCheckSumOf(FILE * infile)
{
short i, j, k, m;
char l[256];
char err = false;
char STR1[256];
while (!feof(infile) && !err) {
fgets(l, 81, infile);
if (l[0] != ':' || strlen(l) <= 11)
continue;
if (strchr(l, '\r') != NULL)
*(strchr(l, '\r') + 1) = '\0';
if ((i = HexValue(strsub(STR1, l, strlen(l) - 2, 2))) == -1) {
err = true;
break;
}
k = 0;
for (j = 2; j < (strlen(l) - 2); j += 2) {
if ((m = HexValue(strsub(STR1, l, j, 2))) == -1) {
err = true;
break;
} else
k += m;
}
err = ((-k & 0xff) != i);
}
return (!err);
}
static void Tick(void)
{
unsigned char i, j;
if (!port_force || hw_timer) {
i = inb(LPTSTAT) & 32;
j = i;
while (j == i)
j = inb(LPTSTAT) & 32;
} else
TickDly();
}
static void Tx6(short d)
{
short i, j, k;
j = d;
outb(0xcc, LPTDAT);
Tick(); /* CK=LO DAT=HZ */
for (i = 1; i <= 6; i++) {
k = (j & 1) + 0xca; /* CK=HI DAT=LSB */
outb(k, LPTDAT);
Tick();
outb((k & 0xfd), LPTDAT);
Tick(); /* CK=LO DAT=DAT */
j /= 2;
}
outb(0xcc, LPTDAT);
Tick();
}
static void Tx16(long d)
{
short i, k;
long j;
j = (d & 0x3fff) * 2;
outb(0xcc, LPTDAT);
Tick(); /* CK=LO DAT=HZ */
for (i = 1; i <= 16; i++) {
k = (j & 1) + 0xca; /* CK=HI DAT=LSB */
outb(k, LPTDAT);
Tick();
outb((k & 0xfd), LPTDAT);
Tick(); /* CK=LO DAT=DAT */
j /= 2;
}
outb(0xcc, LPTDAT);
Tick();
}
static void ResetProg(void)
{
/* Exit & re-enter program mode to reset cp */
outb(0xe4, LPTDAT); /* 11100100 RB6=HZ RB7=HZ CLR=HI */
Dly(100);
outb(0xf4, LPTDAT); /* 11110100 RB6=HZ RB7=HZ CLR=LO */
Dly(100);
outb(0xd0, LPTDAT); /* 11010000 RB6=LO RB7=LO CLR=LO */
Dly(100);
outb(0xc8, LPTDAT); /* 11001000 RB6=LO RB7=LO CLR=12V */
Dly(100);
outb(0xcc, LPTDAT); /* 11001100 RB6=LO RB7=HZ CLR=12V */
Dly(100);
cp = 0;
}
static void InitProg(void)
{
/* Assert program mode */
/* Bulk erase */
outb(0xf4, LPTDAT); /* 11110100 RB6=HZ RB7=HZ CLR=LO */
Dly(100);
waddch(mainw, '.');
outb(0xd0, LPTDAT); /* 11010000 RB6=LO RB7=LO CLR=LO */
Dly(100);
waddch(mainw, '.');
outb(0xc8, LPTDAT); /* 11001000 RB6=LO RB7=LO CLR=12V */
Dly(100);
waddch(mainw, '.');
outb(0xcc, LPTDAT); /* 11001100 RB6=LO RB7=HZ CLR=12V */
Dly(100);
waddch(mainw, '.');
/* MicroChip recommended initialisation procedure */
Tx6(0); /* load configuration */
Tx16(16L); /* Code Protect off */
Tx6(6);
Tx6(6);
Tx6(6);
Tx6(6);
Tx6(6);
Tx6(6);
Tx6(6); /* inc to 2007 */
Tx6(1);
Tx6(7); /* mystery commands */
Tx6(8); /* start programming */
Dly(30);
Tx6(1);
Tx6(7); /* mystery commands */
Dly(100);
waddch(mainw, '.');
ResetProg();
Tx6(9);
Tx6(8);
Dly(30);
ResetProg();
}
static void LoadPData(long d14)
{
Tx6(2);
Tx16(d14);
}
static void LoadCData(long d14)
{
Tx6(0);
Tx16(d14);
cp = 0x2000;
}
static void LoadDData(long d14)
{
Tx6(3);
Tx16(d14);
}
static void StartProg(void)
{
Tx6(8);
Dly(30);
waddch(mainw, '*');
}
static short ReadPData(void)
{
unsigned short i, k;
long j;
Tx6(4);
Tick();
j = 0;
for (i = 1; i <= 16; i++) {
outb(0xce, LPTDAT);
Tick(); /* clk hi */
k = inb(LPTSTAT);
outb(0xcc, LPTDAT);
Tick();
j = j / 2 + (k & 64) * 512;
}
return ((j & 0x7ffe) / 2);
}
static short ReadDData(void)
{
unsigned short i;
long k, j;
Tx6(5);
Tick();
j = 0;
for (i = 1; i <= 16; i++) {
outb(0xce, LPTDAT);
Tick(); /* clk hi */
k = inb(LPTSTAT);
outb(0xcc, LPTDAT);
Tick();
j = j / 2 + (k & 64) * 512;
}
return ((j & 0x7ffe) / 2);
}
static void SeekAddr(long a)
{
while (cp < a) {
Tx6(6);
cp++;
}
}
static char Blow(long a, long drq)
{
char ok, doit;
long d, q;
char STR3[5], STR5[5];
d = drq;
ok = true;
/* dont set protect bit half way through programming */
if (a != 0x2007)
d = drq;
else {
d = drq | 0x10;
conf = drq & 0x1f;
}
if ((cp < 0) || (cp > a))
ResetProg();
doit = false;
waddch(mainw, '\015');
clrtoeol();
if (a < 0x400) {
doit = true;
wprintw(mainw, "Program memory");
}
if (a > 0x1fff && a < 0x2004) {
doit = true;
wprintw(mainw, "ID location");
}
if (a == 0x2007) {
doit = true;
wprintw(mainw, "Config Word");
}
if (a > 0x20ff && a < 0x2140) {
doit = true;
wprintw(mainw, "EE Data memory");
}
if (!doit)
wprintw(mainw, "Illegal");
wprintw(mainw, " Addr: %s := %s", HexStr4(STR3, a), HexStr4(STR5, d));
if (!doit) {
wprintw(mainw, " ! skipped\n");
return ok;
}
if (a > 0x1fff && a < 0x2008)
LoadCData(d);
if (a > 0x20ff && cp < 0x2000)
LoadCData(0L);
SeekAddr(a);
q = -1;
if (a > 0x20ff)
q = ReadDData();
if (a < 0x2008)
q = ReadPData();
if (a > 0x1fff)
q &= 0xff;
if (a == 0x2007)
q &= 0x1f;
if (a > 0x2000 && a < 0x2007)
q &= 0xf;
if (q != d) {
if (a > 0x1fff && a < 0x2008)
LoadCData(d);
SeekAddr(a);
if (a < 1024)
LoadPData(d);
if (a > 0x20ff)
LoadDData(d);
StartProg();
GPCounter++;
}
q = -1;
if (a > 0x20ff)
q = ReadDData();
if (a < 0x2008)
q = ReadPData();
if (a > 0x1fff)
q &= 0xff;
if (a == 0x2007)
q &= 0x1f;
if (a > 0x2000 && a < 0x2007)
q &= 0xf;
if (q != d) {
ok = false;
wprintw(mainw, " ! NOT VERIFIED, Device = %s\n", HexStr4(STR3, q));
}
return ok;
}
static char Vfy(short a, short drq)
{
char ok, doit;
long d, q;
d = drq;
ok = true;
/* dont set protect bit half way through programming */
if (a != 0x2007)
d = drq;
else {
d = drq | 0x10;
conf = drq & 0x1f;
}
if (cp < 0)
ResetProg();
if (cp > a)
ResetProg();
doit = (a < 0x400) | (a > 0x1fff && a < 0x2004) | (a == 0x2007);
doit |= (a > 0x20ff && a < 0x2140);
if (!doit)
return ok;
if (a > 0x1fff && a < 0x2008)
LoadCData(d);
if (a > 0x20ff && cp < 0x2000)
LoadCData(0L);
SeekAddr(a);
GPCounter++;
q = -1;
if (a > 0x20ff)
q = ReadDData();
if (a < 0x2008)
q = ReadPData();
if (a > 0x1fff)
q &= 0xff;
if (a == 0x2007)
q &= 0x1f;
if (a > 0x2000 && a < 0x2007)
q &= 0xf;
ok = (q == d);
return ok;
}
static void ShowDeviceConfig(void)
{
short i;
char tmp[20];
char STR3[20];
ResetProg();
LoadCData(0L);
*tmp = '\0';
for (i = 1; i <= 4; i++) {
snprintf(tmp + strlen(tmp), sizeof(tmp)-strlen(tmp)-1, "%c", Hex((int) (ReadPData() & 0xf)));
Tx6(6);
}
Tx6(6);
Tx6(6);
Tx6(6);
i = ReadPData() & 0x1f;
wprintw(mainw, "Config word: %s\n", HexStr2(STR3, i));
wprintw(mainw, "Hex ID nibbles: %s\n", tmp);
wprintw(mainw, "Code Protection: O");
if ((i & 16) != 0)
wprintw(mainw, "FF\n");
else
wprintw(mainw, "N\n");
wprintw(mainw, "Power-up timer: O");
if ((i & 8) != 0)
wprintw(mainw, "N\n");
else
wprintw(mainw, "FF\n");
wprintw(mainw, "Watchdog timer: O");
if ((i & 4) != 0)
wprintw(mainw, "N\n");
else
wprintw(mainw, "FF\n");
wprintw(mainw, "Oscillator type: ");
switch (i & 3) {
case 0:
wprintw(mainw, "LP\n");
break;
case 1:
wprintw(mainw, "XT\n");
break;
case 2:
wprintw(mainw, "HS\n");
break;
case 3:
wprintw(mainw, "RC\n");
break;
}
waddch(mainw, '\n');
outb(0xe4, LPTDAT);
}
static short ReadDeviceConfig(void)
{
short i;
ResetProg();
LoadCData(0L);
for (i = 1; i <= 7; i++)
Tx6(6);
i = ReadPData() & 0x1f;
outb(0xe4, LPTDAT);
Dly(100);
return i;
}
static void ProtectDevice(void)
{
short i;
ResetProg();
LoadCData(conf);
for (i = 1; i <= 7; i++)
Tx6(6);
Tx6(8);
Dly(100);
}
static char TestHardware(short addrs)
{
static short j, i;
LPTDAT = addrs;
LPTSTAT = addrs + 1;
outb(0xe4, LPTDAT);
wprintw(mainw, "0x%x", addrs);
Dly(100);
j = 0;
for (i = 1; i <= 10000; i++)
if ((inb(LPTSTAT) & 32) == 0)
j++;
return (j > 200 && j < 9800);
}
static char *
GetDeviceProgBlockString(char *Result, short adr)
{
short i, j, k, l;
unsigned char tmp[81];
unsigned char STR1[20], tmp2[20];
snprintf(tmp, sizeof(tmp), ":10%s00", HexStr4(STR1, (long) adr * 2));
k = 0x10 + (adr * 2) / 0x100;
k += (adr * 2) - 0x100 * k;
for (i = adr; i <= adr + 7; i++) {
SeekAddr((long) i);
j = ReadPData();
l = j / 0x100;
l += j - 0x100 * l;
k += l;
HexStr4(STR1, j);
strcpy(tmp2, STR1 + 2);
strcat(tmp2, STR1);
tmp2[4] = '\0';
snprintf(tmp + strlen(tmp), sizeof(tmp)-strlen(tmp)-1, "%s", tmp2);
}
snprintf(tmp + strlen(tmp), sizeof(tmp)-strlen(tmp)-1, "%s", HexStr2(STR1, -k & 0xff));
return strcpy(Result, tmp);
}
static char *
GetDeviceCFGBlockString(char *Result)
{
short i, j, k;
unsigned char tmp[81];
unsigned char STR1[20], tmp2[20];
ResetProg();
LoadCData(0L);
strcpy(tmp, ":08400000\0");
k = 0x08 + 0x40;
for (i = 1; i <= 4; i++) {
j = ReadPData() & 0xf;
k += j;
HexStr4(STR1, j);
strcpy(tmp2, STR1 + 2);
strcat(tmp2, STR1);
tmp2[4] = '\0';
snprintf(tmp + strlen(tmp), sizeof(tmp)-strlen(tmp)-1, "%s", tmp2);
Tx6(6);
}
snprintf(tmp + strlen(tmp), sizeof(tmp)-strlen(tmp)-1, "%s\n", HexStr2(STR1, -k & 0xff));
Tx6(6);
Tx6(6);
Tx6(6);
i = ReadPData() & 0x1f;
HexStr4(STR1, i);
strcpy(tmp2, STR1 + 2);
strcat(tmp2, STR1);
tmp2[4] = '\0';
k = i + 0x02 + 0x40 + 0x0e;
snprintf(tmp + strlen(tmp), sizeof(tmp)-strlen(tmp)-1, ":02400E00%s%s", tmp2, HexStr2(STR1, -k & 0xff));
strcpy(Result, tmp);
ResetProg();
LoadCData(0L);
return Result;
}
static char *
GetDeviceEEBlockString(char *Result, short adr)
{
static short j, i, k;
static unsigned char tmp[81], tmpa[81];
static unsigned char STR1[20];
static unsigned char STR5[20];
adr += 0x2100;
k = 0x10 + (adr * 2) / 0x100;
k += (adr * 2) - 0x100 * k;
snprintf(tmp, sizeof(tmp), ":10%s00", HexStr4(STR1, (long) adr * 2));
*tmpa = '\0';
for (i = adr; i <= adr + 7; i++) {
SeekAddr((long) i);
j = ReadDData() & 0xff;
k += j;
snprintf(tmp + strlen(tmp), sizeof(tmp)-strlen(tmp)-1, "%s00", HexStr2(STR5, j));
}
snprintf(tmp + strlen(tmp), sizeof(tmp)-strlen(tmp)-1, "%s", HexStr2(STR1, -k & 0xff));
return strcpy(Result,tmp);
}
static void usage(void)
{
wprintw(mainw, "\nUsage: pp84 [-apvr012t] hexfile\n");
wprintw(mainw, "\n -a -- don't ask for keypress on finish\n");
wprintw(mainw, " -p -- skip programming pass\n");
wprintw(mainw, " -v -- skip verify pass\n");
wprintw(mainw, " -r -- device readback\n");
wprintw(mainw, " -0 -- force using port 0x3BC\n");
wprintw(mainw, " -1 -- force using port 0x378\n");
wprintw(mainw, " -2 -- force using port 0x278\n");
wprintw(mainw, " -t -- force hardware timing\n");
wprintw(mainw, " --help -- display this help screen\n");
keyw = true;
done();
}
void main(int argc, char *argv[])
{
/* --------------- MAIN --------------- */
short n, i;
FILE *hexfile;
long addrs, datval;
char fname[256], l[256];
short err;
char STR3[256];
char STR6[256];
char STR7[20];
struct sched_param schp;
struct stat sbuf;
char got_name = false;
char prog_on = true;
char vfy_on = true;
char read_on = false;
int argn;
char parm[256];
char *sp;
char got_port = false;
char lp0_lck = false, lp1_lck = false, lp2_lck = false;
hexfile = NULL;
LPTDAT = lp1_add;
LPTSTAT = lp1_add + 1;
schp.sched_priority = SCHED_RR;
setpriority(PRIO_PROCESS, 0, -19);
sched_setparam(0, &schp);
signal(SIGINT, done);
signal(SIGTERM, done);
keyw = true;
initscr();
cbreak();
noecho();
mainw = newwin(0, 0, 0, 0);
immedok(mainw, TRUE);
scrollok(mainw, TRUE);
werase(mainw);
wprintw(mainw, "\n *** PP84 - PIC16C84 in-system prototype programmer ***\n");
wprintw(mainw, " *** 31/03/97 Steve Marchant, Nottingham University ***\n");
wprintw(mainw, " *** %s Laszlo Nemeth v%s Linux ***\n\n", RELDATE, VERSION);
for (argn = (argc - 1); argn > 0; argn--) {
if (strstr(argv[argn], "-") == argv[argn]) {
memset(parm, '\0', sizeof(parm));
strsub(parm, argv[argn], 2, 200);
if (strcmp(parm, "-help") == 0)
usage();
for (sp = parm; *sp; sp++) {
switch (*sp) {
case 'a':
keyw = false;
break;
case 'p':
prog_on = false;
break;
case 'v':
vfy_on = false;
break;
case 'r':
prog_on = false;
read_on = true;
break;
case '0':
port_force = true;
lp0_use = true;
break;
case '1':
port_force = true;
lp1_use = true;
break;
case '2':
port_force = true;
lp2_use = true;
break;
case 't':
hw_timer = true;
break;
default:
wprintw(mainw, "Invalid parameter -%c \n", *sp);
usage();
}
}
} else {
memset(fname, '\0', sizeof(fname));
strncpy(fname, argv[argn], sizeof(fname) - 1);
got_name = true;
}
}
wprintw(mainw, "Delay loop cal: ");
DlyCal = dlyrecal();
wprintw(mainw, "%li \n", DlyCal);
Dly(100);
conf = 0x1f;
fd0 = open("/dev/lp0", O_RDWR | O_NONBLOCK);
lp0_lck = (fd0 < 0);
fd1 = open("/dev/lp1", O_RDWR | O_NONBLOCK);
lp1_lck = (fd1 < 0);
fd2 = open("/dev/lp2", O_RDWR | O_NONBLOCK);
lp2_lck = (fd2 < 0);
if (port_force) {
if (lp0_use)
LPTDAT = lp0_add;
if (lp1_use)
LPTDAT = lp1_add;
if (lp2_use)
LPTDAT = lp2_add;
wprintw(mainw, "\n");
wprintw(mainw, "Forcing programmer to use port 0x%x\n\n", LPTDAT);
if (hw_timer) {
wprintw(mainw, "!!! If you specified the incorrect port CTRL+C will !!!\n");
wprintw(mainw, "!!! exit the program gracefully !!!\n\n");
}
if ((lp0_use && lp0_lck) || (lp1_use && lp1_lck) || (lp2_use && lp2_lck)) {
wprintw(mainw, "*** Error port is locked or doesn't exist. Exiting\n");
keyw = true;
done();
}
} else {
wprintw(mainw, "Looking for PROG84 on LPT port: ");
if (!lp1_lck && !got_port) {
ioperm(lp1_add, 8, 1);
ioperm(lp1_add + 1, 8, 1);
lp1_use = got_port = TestHardware(lp1_add);
ioperm(lp1_add, 8, 0);
ioperm(lp1_add + 1, 8, 0);
waddch(mainw, ' ');
}
if (!lp2_lck && !got_port) {
ioperm(lp2_add, 8, 1);
ioperm(lp2_add + 1, 8, 1);
lp2_use = got_port = TestHardware(lp2_add);
ioperm(lp2_add, 8, 0);
ioperm(lp2_add + 1, 8, 0);
waddch(mainw, ' ');
}
if (!lp0_lck && !got_port) {
ioperm(lp0_add, 8, 1);
ioperm(lp0_add + 1, 8, 1);
lp0_use = got_port = TestHardware(lp0_add);
ioperm(lp0_add, 8, 0);
ioperm(lp0_add + 1, 8, 0);
}
if (!got_port) {
wprintw(mainw, "\n\n*** Hardware Error, cannot detect PROG84 oscillator\n");
wprintw(mainw, "*** Is programmer attached? Do you have permission to the ports?\n");
if (lp0_lck || lp1_lck || lp2_lck) {
wprintw(mainw, "*** Port(s) ");
if (lp0_lck)
wprintw(mainw, "0x3bc ");
if (lp1_lck)
wprintw(mainw, "0x378 ");
if (lp2_lck)
wprintw(mainw, "0x278 ");
wprintw(mainw, "locked or non-existant\n");
}
keyw = true;
done();
}
}
if (!lp1_use && !lp1_lck)
close(fd1);
if (!lp2_use && !lp2_lck)
close(fd2);
if (!lp0_use && !lp0_lck)
close(fd0);
if (lp1_use) {
LPTDAT = lp1_add;
LPTSTAT = lp1_add + 1;
}
if (lp2_use) {
LPTDAT = lp2_add;
LPTSTAT = lp2_add + 1;
}
if (lp0_use) {
LPTDAT = lp0_add;
LPTSTAT = lp0_add + 1;
}
ioperm(LPTDAT, 8, 1);
ioperm(LPTSTAT, 8, 1);
waddch(mainw, '\015');
clrtoeol();
wprintw(mainw, "Using port: 0x%x \n", LPTDAT);
if (!got_name) {
wprintw(mainw, "No HEX file name specified, do a reset cycle...\n");
outb(0xf4, LPTDAT);
Dly(100);
outb(0xe4, LPTDAT);
done();
}
if (read_on) {
if ((ReadDeviceConfig() & 0x10) == 0) {
wprintw(mainw, "Device is code protected, cannot Readback.\n");
keyw = true;
done();
}
ResetProg();
wprintw(mainw, "Opening %s for device capture\n", fname);
seteuid(getuid());
setegid(getgid());
hexfile = fopen(fname, "w+");
seteuid(0);
setegid(0);
if (hexfile == NULL) {
wprintw(mainw, "Can't create output file!\n");
keyw = true;
done();
}
for (i = 0; i <= 127; i++) {
GetDeviceProgBlockString(l, i * 8);
fprintf(hexfile, "%s\n", l);
}
GetDeviceCFGBlockString(l);
fprintf(hexfile, "%s\n", l);
for (i = 0; i <= 7; i++) {
GetDeviceEEBlockString(l, i * 8);
fprintf(hexfile, "%s\n", l);
}
fprintf(hexfile, ":00000001FF\n");
outb(0xe4, LPTDAT);;
fclose(hexfile);
wprintw(mainw, "\nReadback done, %s saved.\n", fname);
if (!vfy_on)
done();
}
wprintw(mainw, "Object file: %s\n", fname);
seteuid(getuid());
setegid(getgid());
hexfile = fopen(fname, "r");
seteuid(0);
setegid(0);
if (hexfile == NULL) {
wprintw(mainw, "Could not open the object file, exiting.\n");
done();
}
stat(fname, &sbuf);
wprintw(mainw, "File Dated: %s \n", ctime(&sbuf.st_mtime));
if (!GetCodeCheckSumOf(hexfile)) {
wprintw(mainw, "Bad Hex file, exiting\n");
fclose(hexfile);
done();
} else
wprintw(mainw, "Code Checksum OK\n");
if (prog_on) { /* Programming cycle */
if ((ReadDeviceConfig() & 0x10) == 0) {
wprintw(mainw, "Code protected part, bulk erasing.");
InitProg();
waddch(mainw, '\n');
} else
ResetProg();
GPCounter = 0;
seteuid(getuid());
setegid(getgid());
hexfile = freopen(fname, "r", hexfile);
seteuid(0);
setegid(0);
if (hexfile == NULL)
done();
err = 0;
while (!feof(hexfile) && err < 5) {
fgets(l, 81, hexfile);
if (l[0] != ':' || strlen(l) <= 11)
continue;
n = HexValue(strsub(STR3, l, 2, 2)) / 2;
addrs = HexValue(strsub(STR3, l, 4, 4)) / 2;
if (strlen(l) <= n * 4 + 10)
continue;
for (i = 1; i <= n; i++) {
snprintf(STR7, sizeof(STR7), "%s%s",
strsub(STR3, l, i * 4 + 8, 2), strsub(STR6, l, i * 4 + 6, 2));
datval = HexValue(STR7);
if (!Blow(addrs, datval))
err++;
addrs++;
}
}
if (err == 0) {
waddch(mainw, '\015');
clrtoeol();
wprintw(mainw, "Programming done: %d locations, no errors", GPCounter);
} else {
waddch(mainw, '\015');
clrtoeol();
}
if (err == 1)
wprintw(mainw, "Programming done: 1 error");
if (err > 1)
wprintw(mainw, "Programming done: %d errors", err);
if (feof(hexfile))
waddch(mainw, '\n');
else
wprintw(mainw, " - ABORTED\n");
} /*Programming cycle end */
if (vfy_on) { /*Verify cycle */
if (!prog_on)
ResetProg();
GPCounter = 0;
wprintw(mainw, "Verifying...");
seteuid(getuid());
setegid(getgid());
hexfile = freopen(fname, "r", hexfile);
seteuid(0);
setegid(0);
err = 0;
while (fgets(l, 81, hexfile) != NULL) {
if (l[0] != ':' || strlen(l) <= 11)
continue;
n = HexValue(strsub(STR3, l, 2, 2)) / 2;
addrs = HexValue(strsub(STR3, l, 4, 4)) / 2;
if (strlen(l) <= n * 4 + 10)
continue;
for (i = 1; i <= n; i++) {
snprintf(STR7, sizeof(STR7), "%s%s",
strsub(STR3, l, i * 4 + 8, 2), strsub(STR6, l, i * 4 + 6, 2));
datval = HexValue(STR7);
if (!Vfy(addrs, datval))
err++;
addrs++;
}
}
waddch(mainw, '\015');
clrtoeol();
wprintw(mainw, "Verify done: %d locations, ", GPCounter);
if (err == 0)
wprintw(mainw, "no errors\n");
if (err == 1)
wprintw(mainw, "1 error\n");
if (err > 1)
wprintw(mainw, "%d errors\n", err);
} /*Verify cycle end */
fclose(hexfile);
ShowDeviceConfig();
if (((conf & 0x10) == 0) & prog_on) {
wprintw(mainw, "Code Protection requested by HEX file, protecting now...\n");
ProtectDevice();
}
outb(0xe4, LPTDAT);
done();
}
pp84-1.1.6b/README 100775 145 145 11516 6573112647 12375 0 ustar nemeth nemeth PP84 - Linux release notes v1.1.6b
===================================
*Prototype programmer for Microchip PIC16C84*
1. General notes
=================
The binary version (pp84-1.1.6b-bin.tar.gz or pp84-1.1.6b-x.i386.rpm) was
built on a Red Hat Linux 5.1 system, with kernel 2.0.35 and glibc 2.0.7.
If you have problems running this, then grab the source (pp84-1.1.6b.tar.gz
or pp84-1.1.6b-x.src.rpm), and compile yourself.
The executable (/usr/bin/pp84) is suid root in order to have access to
the parallel ports, even when run by a normal user. If you feel it a
security hole, then remove the suid bit. Oh, well someone will burn
your INHX8M format rocket launcher code into a PIC... :-) Seriously,
the code is not verified for security, it may contain buffer overflow
situations, which I don't know about. If you remove the suid bit, then
su to root for running it.
The input format is INHX8M, the addresses are compatible with Microchip's
MPASM output. Further description on what's programmed where, is in
pp84.doc. Maybe a man page will be available sometime.
It's possible to read the code from a PIC into an INHX8M file, which
can be programmed into another one later.
Description of the command line options is available with 'pp84 --help'.
URL: http://www.nexus.hu/linux/pp84/index.html
2. Hardware
============
There is a simple parallel port in-system prototype programmer,
designed by Steve Marchant, which is a modified Microchip Inc. AN589
thing. The schematics is in the documentation directory (prog84.pdf)
along with a sample 16C84 design (pic84.pdf) and a sample assembly
code (pic84.asm). I programmed dozens of PIC's with this simple
hardware, without a single error.
In case you have problems with the auto-detection of the hardware,
then you can use the force port command line options. This shouldn't
be needed in any case, you most likely have a hardware problem if
auto-detection doesn't work.
You can't use parallel ports, which are not recognised by the
kernel at boot time.
Theoretically you can use PP84 hardware with no on-board NE555
oscillator. In this case you must force the software to use a
specific parallel port. The software timing might not work on
very slow computers, but I think a 386SX/33 will do the job.
Programming on a board without the oscillator is not recommended
at all, it's just for helping you out, in case you don't have the
components handy. This may reduce the number of reprogramming
cycles due to the somewhat incorrect timing.
In the very unlikely case, that autodetection doesn't work, or
unreliable, but the oscillator works otherwise ok, you have
to force the programmer to the appropriate port, and use the '-t'
switch to override the software timer, and use hardware timer
instead.
3. Compilation
===============
In case you need to compile the source for yourself, the usual procedure
applies.
make clean
make all
make install
Make sure, you are root for the install part.
After this you might want to move the example PIC asm code and the docs
to somewhere you'll find them later...
If using Red Hat, just get the src.rpm, and 'rpm --rebuild pp84*src.rpm',
and everything will be fine. Now I've put my hands on a Maximum RPM book,
so my forthcoming RPMs may be more professional :-)
Btw. this code is ix86 specific, but it can be modified to run on other
platforms as well. Anyone?
4. Bugs
========
This software is BugFree(TM), of course. Still, if you find one report
it to Laszlo Nemeth <nemeth@mzperx.ddns.org>. This only applies to the
Linux version. Not only bug reports, but bug fixes are also welcome, I
will include your name in the doc and the source code. ;-)
If you experience problems that might be related to incorrect timing,
then (1.) do programming on a less loaded system, (2.) fix the code,
and send me a copy. I've done some testing on a well loaded system, and
experienced no problems.
5. Legal stuff
===============
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The original Turbo Pascal source was kindly provided for me
by Steve Marchant. The C code is derived from his code.
PIC, 16C84, MPASM are trademarks of Microchip, Incorporated,
other brand names mentioned are trademarks of their respective
owners.
Laszlo Nemeth
<nemeth@mzperx.ddns.org>
pp84-1.1.6b/pic84.pdf 100775 145 145 242124 6345102360 13144 0 ustar nemeth nemeth %PDF-1.1
%
1 0 obj
[/CalRGB
<<
/WhitePoint [0.9505 1 1.089]
/Gamma [1.8 1.8 1.8]
/Matrix [0.4497 0.2446 0.02518 0.3163 0.672 0.1412 0.1845 0.08334 0.9227]
>>
]
endobj
3 0 obj
<<
/Length 81356
/Filter /LZWDecode
>>
stream
ph(b 4 P a
D"l "#Q@mHiD%-&29,b2q
SPCHCCZP
PjV)'@@(f2 @F7K{MkmlwKUh9u(BKb1Xv
ck\ƶ+W+6Ig3K"#DžEas7<.'u<>(e
0m$`ox]8nK۾/[ì2.+ߠ3t;0ªF"`8NJZ*Haű$=S=<
Lw:OHB-
ҋC(
d
JLC b0sK=62<2E3t
p;q]Q!EQ.S0(7OXhYTT5[WS}CDNp]L庉;ʱS,9j$jz (3VdrrvmVlOMpiLE!tZp]@TaO._mWoY+T3خ&,n8$N7l9;WwYyN-s"fZAyc
#Y!Q5%;+57tW՞DrF Z^U%
3*;^izn}jG活۶ŭg+f=Ͻ%-5)&t4QiB^2q8{>uTa,x˿zu-T{KbԶH5
"i~kJR~np]92+տW 2RJpemԢPC5RfM\&Jн7e6\cY9ʼnaezhm
AvPydޔXOBQ-DxbGI(N(%D[F>,%ⶁq,Z,5v>
#[,8o:4_ug}q.$"bɑNJFCS;&dlՌ%X0z0(eذx^RdA_&ıxM%l;l3EqELy0P5olI39s϶:%X<U5&:H@H 'Ɛ@BP̨4$v1&\>,|K/d"4\LL\qO{>230d*hc$3
!24jnpiLˌZcDivRPTBe3\0$V+!*5,&j$5Zj1(5QK3Om$٥Zu<0Li$j\\k*䆥ڿXjU1zN[HsǜC:}TlfyT*m"wRK[ar_c$䲃;KS5PT%EjKL95`<^KJSY;WvN.YjNˍHh%l 5"$PohіfߚWeyˀnY4JS&G6:]Ipj@"2|R/x.ڌťNjU%؆k[B8pbGu8a_M0~>:uo͢Xc_G9NW.y2mxf/#Ι8Y52ӰκuRCke[,2|]$apQE8~ڡ3{vnBycOnNZ̈I/CZmSGIjH9?vcA56VoexauU;Bm(bϫz>LIk5r{:ݕIsf"(vgOeZJ٣25&&U#c_4gjNY~fZA¡ξQskhY #{1{
פx''8kW/=GIVdTm]=A;L>ZS:Y}grD|]\5;o[==3߅0c5g>$(N fU)?ĤTLlxlG:rotFeꞯ;&Q+pwlB+ԡZŭ]|Ϧ~8N>)
sz(JVDLiN
zʯfn$eb:$k.ﰥe~t 6[JjjN:/RfOv>πeJ|jLeT(v_:Fg/äJzpt3UCPY>dΐ觌)]̨3Cjp5
+
"PX䯪pe)"m20m(Q0<$ЮkH&ĬUo|\RԬkV2/#3np<//~0"Fj~f
)$
gMg(%sqD01t$-XQʹϯ sk.q
jEV2/| Ѻ
cљ*8LMv2pcj^ov_K
Wvތ1w6G $
F Ty~-bxi?$n &o-om&;~䢥|fTU?'<4v&OrN&J%I&&zq>*VM,ŦIF.htxU(eFo!IERW0$qo.#0Nбs8HRɪOs&2
Ql8@qxN0.<m#DX%q Q`EkDE'*:xh&:3r719,q6=6QDsVrSF50QhB8lm:=sH8IQ 4-F|@3Mk%5D0=9S@&06QsR)4͙4̓)/qXrSTӹBC,Ƞ"""E.Md7Bds0L93d 3]6tZԓ]=ep$Je7s \6]Ɯrt~$qDRc
(*6+b0^6N/Cpb(yF֣BS@o0t(ICqLL̎Q7E4ҾLĎQ,
KQ7e>3KUo4+N:Tp"%tGAMN*SO TT#\rO1CR '$jDc2g.D4(`߯$COup*@XpD(2N$CBjUk0
Pljf%պS[KOQ3X5|
a]l8[YVc4WLM+>tS426$Π?!0-{]d0o`2/բL2/g3&D~SD%dpAH#W(t-]Fj6v*
Kc=3:Ed2I-^vE2CcgVf͏bpa*u5vu*a*\im&<Ymf4u#BY7Wu:wfihoEn?)Ħig\Go҂w5h1n3rlm;pY)H&T>qLn1OXR-p6w[#i#x/a1̒]p薭xd63vGRuKuiU[MJEHK3]5J|7M3U= 3=p"7`>O3xu
r$NrRQBi&ĻeQ$6dHɀjD5c'0'z5Ԏ{븣C$
}QK JT3xXT"EPnvqÞa)_GCRI-Z*6P!KqEL=>3~&0^"-ȹh-,
:JN֚z_yq>8ڮ^M?
J
;dX}oQc#H=R,~0U6Me8ӪQ583k]l,y"Mx:;yO k82m+mϖ;wԣcUM
evذ%% R~߹~iedYf)UP|֫%yE6_D'9L!D9ٷĽw
clj9w+
:.=9A+x>>#eK53LB?9!Wq:9iVLTdߣ$3b)>n!Mc~&g_ p3!["JwZ,ZzYZX.958yXw3z2C8gOvK{b33thL%gue-:"!C^;0 #B+B)Q'=Q(LSstl;k7["lDCM6T8vbMX;,=bHuvTuBL-3E{X6ڠAl-'` P
W{4`SOWLRC#ջ4T`E|E1ɬ;:gŜ/ê_3;om1z[
?R\cL8WVc{1
h1<\Ǧo+'F6'%HSx90E`ZDXNy<0]}A;bT#|#I<%d<ɝA
7Wp˻CiU0/_]A+d,mg]veu0Un{ݟ1[{\E[fn[̂I%N'Zߋ
\9EG|K+L"U ;Pick$!/1S4^wK>4.;; {.%T~2(]ݵedۻۧf9^x&Z>BYiVBO]{YfM螚ލYY#$l IS됷}b8N=(X_D'L.Zf}-Ķ&"1У^X#qڷ>R0"~cMe~Q-1);,[!ia!L<W?k۩ͭOU?TbXz5G/e\9IY֝tBi:"G@Ofų7H]0E=fQ3-[0w#ȟ?ѳ .j.`44
xl7C(Q9d0qȸ`3CE821o<ey}4OR \8bU7zڠ6A`dn`Xqgիa6)k*iQ.ҽipDm*ŌW s:J6;_3_q6idk}^7}紲
r)CU8AeFA[EsgM#
?Nw{JQAF{}V2'$zrS!*fNkڄh
Я+h Ҋa°'Z 0[rA
6+#;oҤfD
2*Xű`2BҢl>*+J1O-jr̊jҪ ཌྷ*%"\7d%".JHtz+ GsH裠Q!;- >ϳtտ)o8(I=T
*;Y(;cQ?*Qϸ`کzhXbҴ,ynX
ڱIUsqoJZJL%\"TyݪYf6Y/u$LuoHX֕ՏH@uc䫂Y#&妯$V`!Heyi 59zu
uI:06h<Lߺ4ޭfkM@i%MLi*,"OdZU2ZFRh-K;٣gCaph(̎gb[<x=}l3j
\4uK}ZW-g_v:d>F`ck$,:I^KRcxk:0;y<Mԝ s54~mq~gB{&SxY{a%#x#@*`ፑ'-hhh&Ga+.:KM$D$&8@WU`Җ%\Tu}
$@q$8!h,@A Ķ#BR2Bȿ|PR._:˄%!t0/GhC
K{<f\xlȌV|d=HS`(R,EB"d|yEC5d!!ҔG("э/2n<I*o$>/IcczD#CTqCeIf&E_ Nc[mMoT\+2ƙ
57H/n2TfTj]
c!i2]2BB"ԞDL8{D'qfu;rY45dL(@OiVQ[rJf|ELCK(BR#>mVdi|U&>}]-%$PIIu25QZ!C2s@%k%RQ$e!O&Uel\Ԋeu
>
TǬJSXkԜB2JUά&2ȷXfUPb<'md(BqH{a28I"
Pb*
\Y5^
n-`X})غP
Eks,(ql\%I "tEА$*1j2^+ʷ*P)&0RU#(0
\v1M·ϹQo=rKwT]xk0s>n**>qh\bY,ҧ(6ė}8ȬZR1Ƙ%[mRRk>QќKUˇ7EHFW5ٙƩdٸQhD͒!DȚޯ:U;bUtQ%їE?1.Œayj\ӥsk8gW/tR`PJۗqD4M윟i7,
=d`N+~?̚[ U5tY?ܱ[Ex*Z:itYFTuK龗#EuM͡]v> ֽq?<1J<B`4sv<.w r&1qh͙tOC(V^'&
<n^xHiajAf6,LogαoW=C!}i^ZZ,UVHmY î7pohW)m"xy$^I&(~6c~;j%;>J:x;7[}?v\ךxv 3ԴbE$rqo寂H6F!&,a1/19PFZ?VId)_j<阁5JL8g7OC1H$ECx2J"!ɢ&!4 .A ?Ф/?\
1/(#.B1C3qdSx'z|Y+4X爼<A.0|B<&1 T$@
q7hݎ\ >(ü7$9 quCבŞD:ÈDyDw!2?AKECk ;:QA~,@B\2C\#DD,,\&$EB 4jI6y
FNDfL_FDV"Db. D`/C҉D3É6 SG'l#>Y%zH 9x#L-3Ǫ~Ȥh{?,~F<ād{8@qd0PlJB18E\1zE/D$:1d ʢҙ4ɃwH!IZ*!ě(IEk TIs1{˒DAE|Kq#BCAED<Di2tJqA
HtG0ǜh>[.%j:9k2+3|z ӵ$ٴx(SHDŽ|j>L=NZJȤ*EdDAK`DC!YͻkM=mˬ,IBjL lL,L|O<Np<4ٱ6,LPԿMB*˅+)7*h7ȁ\NN5@L::-!ԙ'#eK3) +9LQ;I4rO
:0QxQQ|PNS]3SNu2:m.̼H&;2G1T;k
@JΫK(Ĥ;d="N3½
<]Ь5OTΛOE(OѼTuLUu=T*mF"IuU!A+A]յD 뽹ʱ{@AR34YE:t3R(X#;R&8
8T8*V=h#
f)
^WiW{HWWX7DJ(!`7XX<)%ؒօqWu}? lEy@4
VY0
YŔיm &mvYp&ŝ
3VAh@ݑ~C5ZqTLpUٯ#X!*Bu5틶r y4 7Zk6ګڝſ!qVM5X=UB~ͦ(|5܍]GUWi\:}¦]|Zٓ?]nLܽ|Ixu}
<U~-XM
\ޜV\^^}Sڕ1YT,
Uױ+|_^~CZՀԈ=5I=#qMr}qLVW1FxB`
E(_ 3V*H8a8LܭV,2VaqH=0.JXq"ń
$8%U-ia wəT,Kj.2*f0
!]3Vn8#V*ɞ&"ɞ)?Rc{B2(?n4b.7 6H:6(^Id+c+PD--aͭueIfEdicI~ZD䨵N&[L6L&N^I
d<BhʬM(Vhfh ]fb mF[g6qd$u>xYvze|cB~ajրͩN)~
Fh`nWתV⮋X+hsg
fa qn:.qŎi~v,4 gi|}vdᎁj^>
k)VΖꆪ莮i覰i9ޓꦬvkN^ah#c3%c.
.*6Ih(kke&n*R&e嶥lXj!PfPgI[gr^/>f.jg*gDVVogNm9Nn[m.cgF}aκ칍hh.eFlvd;^^kfjα.&6al1궅kldꮧo_el}mF$l(w~moah疟p~9n->Iv7j_qwv8phwq>~~lqeG~d.lGw#M/rqN՜v0p/&w2oV,r^ng;n#4t
Ϥ@ex"k8_b R?Di5K/ASoM jts_#tl?Q>u>*nUjCjZjȫ
[^XuƬ+vRh.!I =tOb`4hGbviEr_XPv -s/^vve I?vw7yw}R[wo
@tjwGx!M(xwpx?vwωeU}Gxrg=xgs?y_koחydTwmߑlxFٯj*R?{kg:CsvgwVrǪ9Dz|mvj_vhz{9
C{a"O]{[zϴk6/ow!|lW6|fo'vߟ7M%^tu'3^>:*ag}nDoy0"kg
{O垏z^zppbw%r~wϫ~so"'nFknޮysh{vk/lƲj@L
Gl @m`\6
X0c C(.FXܢ@6
Ԟ;)a)Ě9OC)*r3
MFATV!AαRU!pkclшp4Za,d10]4}]rtms}O+*M6UT7K;K7:hف{lǧIt7NfG5o.WugT~榗Z%ZJ)
)ԼzAh+k@!ű e1l2⤽ k
DkpcDZ-QESō`j)74ď<p\PlǾLF|8;jF3EG1H4%&brb:JxNhMS+ЫaB%R쾘IT=-QҬ1D/(AJ{Q:jS3+ NC'4cHVYQ7V5OĪ:Y>ތ6CMsI^єE/ut
7=nbʓzW}VQhwj37ZbmoGjpq@-pXXt$B,)\Ld4wYbټэdY%Wxe fW]WH6}'5`_/.Y2[FFmqSӎ{yZ8iX[YR%}y6a Zԟ,B/UkFrs*r`o"մ_%ȆU۹=S7=ύ/Nq2Ur+ZE\tgwGb=}_[AG-4l~_1%?to a^.u;NpyÕ$8)40 j<{b!zuϠsF
eA 0R)81!ķHOOh8؉7,X`2tq884$h!z1H2K!N0@iHQOhDثMAЌ1r7SFdAN/pqF$S1m!8 -s ;Y"O%0(d!Z ;$\
$UK|BHE)%N&r*I,`@jt[՚3jn$WuH0~X\!!kvS;9S3SБKs C m,#r!I3rXᢜ2Sz
8ٶz(tIQA47.O?diOK&"39LIb12.IәPC4j㢥 1RR@TEiӮGQ'kuQ4}QX),1JdzV|
η&NЋ
WC4xW)b{.LYjȏnIʰ\\:1ST㫼{3 0d.N=ۛv#12+mc"W.Q쬁[.=|[V
oE㮽0My˥{A*
2-+KnTegM&!b /lS^ˡnӃIo.r+7~裃|q*GFM뽴͌~qT#ّMNƘY&J\)K`C"yB>*e~).b&ҡ0`vXpj^dz26-yFb,Z+ƴw0<y`vay: LʺVLC]KȘKHfiW/DsvץR"ȼn^ܴAaj8Olz`~+U}nf7@+%C~FS_k,pf$2*c=7ԡ4Bm.L{:--Pou|oq۶:24<M"p&͓)#V#Þ)>[B6w,E}Z|{J.Vph{')e͋6Vk.HJyc!.3_(*V<Dڑn8>PŶm $l&dh<Mv+dm2Y}$W̿弃Ǚ?]{iPxB1(rɭ~Ҽ^7~˞:cwݏ>;P"OGvIU1R6H\ŏErE/=jLPgh珋鶷.,&,9oJ&' «+ pd*NuKH2@PRHlNЂmH
'o^nO!py 0hp[oГsp\/%Pe)AY/<2m`dghpһM<cdf͋rNLKw/%1 4
./H1q-ElG*x
TΐBd0[g+߄*Pb+tш..xd*&o oeK uQRyHhgZnf|j*,qs$)2/AVQ%v,>\qGk(ܱDR,O5$(v%M
/rD# 2vf(4 "͑! (&R.h>r?)<0C'A)/J)!+Vk2/JQ60cfQH0cHΔ+.rڥ|FR fM3bn!i02f/BI*13,DBHPrSTlG22qu17i6l.-1614X10s/Ppz)*ΏEMX֮Ӵ1F$ghqpsu& =N7vs~{0s%kZ17<N.sz˰$p>FC=u0Vs/! b$lF&BF)6T1?=$k ?PmCI2B;Ek)2~n6FRgA%?!)A6Ԃ4bui4;f+BlI͌bu)Bv}<b-J'X4*nqMT7L͛MINU?BJ9eJrlHmJlEyJJTуP-Gjz0¬~uN P3QiUMh%.9u72"_23G(*pƽVONJL8QT>d),7#t5U40-K3v
<w\+E\\y7D\RqX~\#tFUNYQ>UKA`,20H3F6%u^յG^mR]^5^4+]PHJL3e)`Toe b)b}vmMy+j̔ry<$D|3/Huh Dg5%gbhя
iYlp6W6bDuO)goGe;PnGhRv7J,-ep%2464&rm֊6
%f
oKldmob}LϷmfF+j0I76NFeuP*ζάde
ysWwr%wq7uqvzr`wpWMg?w}ZJnflFmr0@=O~vҲɪswl^.ɶ*\e倶U~+Q}GfvLp
n))kb*1 '3yx8GWkjjQBDk5* cMJ&dp`x/kiEPLRhvqeo0xq@Ʒ$Hh\oaWBt:fjyP)YUΕ?(xr^gf3G{JLfF&39 9W)QcfXՒEqoB$F<ky "Q9W3EBy7]VX܂Ǥd×ljQ<7&ɑ
Vٖ?sB9r{~r|$v`+GkeMfÛKr6"Ýfͅ8Tg1^LrŦ2J`ijObMZP'0s2;
e CSEmwER:(]Ěz`m!M (i8ݚOJcS,^GBIh ֎tQk7&fDM+<wwdlޕZ<ZɴfjdA7kX014apOftOv:]Uk'B cFÉ+q{[ܭrR%$4-~5k`nzo(IX#DskxyuGcǫ)hҕP;&,G{#p~XrFcpwt},DΈ$V!0ӎ
'i^;{8'2~Yj*绬p\ /"[}m&i[Oe*D=ơ2X[%[)eϺ{od;u8^(ԛi\`2FBŐ3U
ף|U<9Ȳ]!\NgE0\9tWhޮfCg|t|1WwmdF'mgwgTCjXiv.7Rn6ҷ}yL}YOTW5s2!}h0}q"|]mrwbYqhz}Iq=
}AV761t5kf.]k=ܬX,Wv/tAagU}Љ{"!qSNq]ݖؾ.q]6Ol)f{Č7D~Rؗv|Hɴ17" ;Z
!u>PWcm$Ho)ފ)^;WqJ]ڇqNY;{~1{[>_P(n^JvJS;vw@H܃vX1ټ5ib6dVk'_iaj*U]腷?/&Vr;wkLH7 sY.ƞqӵ@dw?oo42M~VׂjEYd1nuh#WhJoC5?y?!p1>d"~A\ ql4a
A]
m8(47C! TJH18k'JAs1qq
spl7D!.
0AquJ4smH,,ᴺ-G*͠q/VхgAx(kImu1zαc1]6R'
~B&ITK'"zn1NQl2՚57
Ń.[3EoA-ޭ;+s(kyC,m@<lt?o2LJR7d߆*t.l33
&@,<4LBHH)B02HJrR)hp+J $!Ipz#!mQ'#/kB4q+M++k/+.hIr%|'
'I4IjK2X)\ȌѐBIRhʮ|Tt/ByP̎5^3|hO@`Y%-R,f,)cZܨ(}[ZًMnbLP%=jT1ll\MpImm<5-'5jgh^+d\˵c1%WHyʙwxo[;Uv*-6BQ:(r<yrm8]ecȵwZ6g?Ƕbk̚2.3편b6@Ӷ[KUؘ&#\[^7 J?C {S
vn[m|0Xf@=i6bՌn)2qǪInMOˊR/TJ}^o'si7P5li!-#[n@Bҹ.A&TZYYZI=8H;Yi+r6skiugveOeU̟$g&@)FW|'mĢ5TeqEI> SVk*gAYY6Hl`
y]C(HTq=a 9M5&\ʦJi',<AMTw1|=RP`4%"U!ԏutc*P)Dc`)*S
$]RX5 XӊZȕ-bњ(YbL[)ʦkVCMB)$dy63Li&U
@mLC+rTJ<d()s='Q@vg=4[ZT E1AKHAJ4JPKԅR G)3ErL(*Ee#AdL!E2d:MM~F:vH[AԋHF
1 ~0QUHDG]^(ijpT+2ӺA"4Q,JVDz^d)薦KEy/3Fa
m{ǨRk쨒l/ejv*C,i5%
cXa]Ƅ6kc)$="FsU-42M`EH,Wee5ß$E;ReBMVx2+VA-:Vg칺h`砹`e).H~ڄ/״g>"/KNsKfh#B_ߋEPī)I+7pjb<Ao276*)$60q.S&,k]&c5&9/};r9s`ZsfJȩMY~f]
nl7kR#Y8'6ti{ :iʲA)qVgL$WYh #9盙Γ2c34z͖rqڙj3nr"Eu-\&Dg-BlL59k{벣Y(unwZ+C;g4~o 7Ct;fe)~$X9"BA,3/՞;넩$',jD(zIԃ5~gۢXM
&_5<P* [x
:
^Ot*ʲ} 1
>x>XV'n}5L9)ܓo
N84.ŧSmJڽm7/pVO_3'ƹ^=
c(DGa4mNRV<%^Dy?|?ͻ3ʯ+5h"mpWǕ۞KѾt!44i?:$yr%}fO54m|_s8[,NVMۧS7]7cHʌBf:o]|xHWn]12Sncd߶ݶC=.kuc
f=˵?3v@:Kz/
:r8@A
h1ZL'kj\zCS+
[;#'{<!Bdd\;20عB9+>s۱p<)'?9 ;[,T2j%k*|CC4{zJtt<4~CP$c4Aò2s=Rj>h1FL{B2XDaDڽQd6{"z-3 22Z^
+<w``7EGˉY%>m7tV`$Li&hC?%tGfv9#E]lE-2GkF<acFjӦ%i7h&"\;jc<Ŕċ&;["|}.fy
˂
SHFh<EĂ:IWcFÞ{JI|q/
,+P.~/!#HbJd $#:TalA܈
zKHԷ˴H|DXI
s|Jh<,6lx"z+yLdɄ1jMLˉҎd"dŜCItKC(?͓͒3N3TP>t*[N:b?*OΐG։DĩD˘D H<f 2:$ ;
@COŝyHʰ-ȹ ӟ̄L#JEHO$uL*.ѼPG,K,PPM-ޭrں tM,,BN40$M32 L1LKGS?++"SldS-Ӎ=,
ӊS>tRC7M$`$T"pTu@TDƤDu,3NkK9Y4lPD0Det>5>مMKJP:R=^յ:L5XLPUcSQ 4j_U$Y{DQ=DU)eFQ\rVNT%n}V'*CMb͍oh͌5oBm\HXE"Ώ䶝jTKl5S4Uyi[WTVx4
,oRer!T ϦuCl Ri{IVȠEXGv91ɁUY#έ";YMڂ!1"7<]<4r XGEFlkbE͵Edm۬aǂ;eۈɫ}Ɍ;4S5q'J-cT\Z%4RD]<Wո\%]A/]u݄]]C ]I\SݼRݼ]9)]]42G]I?u
U9xؤ@l?^]{M]9
@VX]U
EEו'PnP'_ڣQu`:r
ib<QLFA4v^QD_%PA\6Wq/]VMȌuލ]uPeU)FN/*KA0aϮصʐv <.+:6Ⰴކ.&4^cUwb3ͭ<F=M0+`ee.*ǎ>X`n>Lvb.%MU0ͨVy4_=թ`!
~Ae
deC)|^N0 VMB4vd݁w7_Y*YlspaSU[cafMC5afL5caC?mfs'@Ρ[]GZnP`)cb0lܕ<վs)hfQ<f~wZN7ԊҩM1F揔#[2;j
J"_@rHfuh㪠RY$xaƞG%{h,r">d8RO%`8^^
hj_l Ua%6^>iRqIFiP
\ꎗB:[8OJ|<)ο9NR{Eޒl\di*'i&
̨yjD̞i⎦*M<'2lj<k6 ;ن뚊C9`mfzl즩]fʥm~l*>r֜
Ҭr~n[m^Yo:ek~k[onʝO6m*z,y!VnઈFn?v호N2`v!67mdOk6a;
qiUw
o;jI(ԑR
'ޡhϺf
Zo
?^r5@6B?jjsER1z,%ɧpGL@'K+sVFӔjQ2
4!:= ۜVLe[=Yʫ
lpUwaG7btGUntbCgv,~GVvyPەD7\$LZdJoTp>T曺!pO$j'Cw}!V=N_r6guDf7֤FvtJ-ԲO~UkbaSM?R jv9w8}iLkkeWzqP*dwĪWOoyZvi'dwy'[3x$$zΫOwPp4M\>~v{Kg{Nzv|;/s{`H`{Gz귥ߌŭwJEϊxEgvv
zPϷ_zUoZ_"},aZf_'tO0t}w-`_uRs{1VctG=}'Zr0wg?uokP,drN}/wp1 d6FC`43A
Ƃl<kF1xDj9B$&'Bc^$2p`2Besvy3cpcQecx-N&9SJ7E:&Y5lmT
*ejU*Z'!\eUMiجd\g=2x,D/Eכ&7XȾl8Vfn/aD
h6ihHC1P3ٌf>͏# 6sf0r:Q>?teFPۛ
G6Y(p!fƮ<=(V'Pn
n>8jp0:
>LFlSsk,Z&j$'i "H%r&s/Z.ڋ-)])(e:
"JSY9Bά#/DK,ki^$GRM:t$!P)AF"H TF[dч(PEDLre*4n);D!PZ:xMa՟e2(6AAir/b+XBqh!דGzEhL钸x>%Δa,\Z1robW8FruAcx?O
K J%!7j4&hB$.yH&Mt֢ı)lӭ[YX͵' (:g~w(pjO Uܕj'fg<J&q5{t6t=NyoMx]/V!+bGe{o*㺻
B@PRqoƅ?گy~uqrh'0X2KJ0%B0b
ENb%kR5a<yP>
ۡKn-v5{ConDJ⊫жs2rE0"4_@H {n r`bI_AABxa\|n
H(d{C9wc,CpR%¾Y(K.'IL1&P$yL52L{ˌR,w).VWKҾSj1L)hTzy381]rቨc|梟2MRO\d1)Ddi28Cjv.U( RKyIޒ1d5KR-2܅AB*ͧSNVz'o !tt D8GOZ%TJNI`*H
ʗҪe8)t2WDN(4(*Q
L'.xTF3Q2>