diff --git a/src/cmd.c b/src/cmd.c index b21e9e1..9c3d92a 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -206,11 +206,11 @@ void parse_command(const uint8_t *s, uint8_t n) pipe.valid = 0; r = pipe.valid; if (have_b) { - if (bflg && cmd_flag('!') || ~r & bflg) { + if (cmd_flag('!') || ~r & bflg) { memcpy(bptr, cmd_buffer, 16); - pipe.valid = r |= bflg; + r = pipe.valid |= bflg; } - else if (bflg) + else goto error; } if (cmd_flag('%')) { @@ -220,15 +220,14 @@ void parse_command(const uint8_t *s, uint8_t n) if (cmd_flag('!')) { bch4369_fini(); memcpy(flash_buffer+64, bch_parity, 16); - pipe.valid = r |= 0x10; + r = pipe.valid |= 0x10; } } if (cmd_flag('<')) { if (cmd_flag('!')) goto send_buffer; if (~r & bflg) goto error; - pipe.valid = r &=~ bflg; - r |= pipe.status << 4; + pipe.valid &=~ bflg; goto send_buffer; } break; @@ -241,23 +240,17 @@ void parse_command(const uint8_t *s, uint8_t n) r = flash_submit_command(cmd_buffer); else r = spi_busy_p(); - if (cmd_flag('!')) + if (cmd_flag('@')) spi_poll(); if (cmd_flag('<')) goto send_buffer; break; case 'P': - if (cmd_flag('@')) + if (cmd_flag('!')) pipe.dest = 0; if (have_b) pipe_config((void*)cmd_buffer, (void*)bptr); - if (cmd_flag('.')) - flash_poll(0); - else if (cmd_flag(',')) - flash_poll(1); - if (cmd_flag('!')) - pipe_poll(); - r = pipe.status; + r = pipe_poll(); break; #ifdef HAVE_FPGA case 'O': diff --git a/src/config.h b/src/config.h index 47dc5e9..c87f9bb 100644 --- a/src/config.h +++ b/src/config.h @@ -38,7 +38,6 @@ enum magic_flags { MAGIC = 0xC5, VERSION = 0x00, #endif - hold_pipe = 0x01, }; extern const struct config config; @@ -159,7 +158,6 @@ void apply_config() extern struct magic { uint8_t magic; uint8_t reset_source; - uint8_t flags; } magic; #if 0 diff --git a/src/dose.py b/src/dose.py index e08cbe7..74a830d 100755 --- a/src/dose.py +++ b/src/dose.py @@ -66,16 +66,6 @@ mmap = memmap(map_fn) class dose_cmd(uart.uart): - def parse_line(self, l): - self.responses.append(l) - v = self._verbose - if v>=4 or v and (l[:1]!=b'#' or b'?' in l.split()[0]): - try: - s = l.decode() - print(f"{self.portname}<- {s}", file=sys.stderr) - except: - print(f"{self.portname}<- {repr(l)}", file=sys.stderr) - def cmd(self, c, d=None, timeout=0.2): if not isinstance(c, bytes): c = c.encode() @@ -93,7 +83,7 @@ class dose_cmd(uart.uart): e = int(r[-1], 16) if len(r)==3: d = base85_decode(r[1]) - if self._verbose >= 3: + if self._verbose: print(f"{r[0]}[{r[2]}] {"".join([f"{b:02x}" for b in d])}", file=sys.stderr) self.last_cmd = c[0:1] return r[0], e, d @@ -101,32 +91,26 @@ class dose_cmd(uart.uart): def poke(self, a, d, s=None, ccp=0): return self.peek(a, s=s, d=d, ccp=ccp) - def peek(self, a=None, s=None, d=None, ccp=0, o=0): + def peek(self, a=None, s=None, d=None, ccp=0): name = None + if a in SFR: + name = a + a, ss = SFR[a] + if s is None: + s = ss + if a in mmap: + a, ss = mmap[a] + if ss and s is None: + s = ss if isinstance(a, str): - aa = a.split('+',1) - if aa[1:]: - o += int(aa[1], 0) - aa = aa[0] - if aa in SFR: - name = aa - a, ss = SFR[aa] - if s is None: - s = ss - elif aa in mmap: - a, ss = mmap[aa] - if ss and s is None: - s = ss - else: - print(f"PEEK: unknown addr {a}", file=sys.stderr) - for k in mmap.keys(): - if a in k: - print(f"PEEK: in RAM we have {k}", file=sys.stderr) - for k in SFR.keys(): - if a in k: - print(f"PEEK: IO SFR we have {k}", file=sys.stderr) - raise ValueError(a) - a += o + print(f"PEEK: unknown addr {a}", file=sys.stderr) + for k in mmap.keys(): + if a in k: + print(f"PEEK: in RAM we have {k}", file=sys.stderr) + for k in SFR.keys(): + if a in k: + print(f"PEEK: IO SFR we have {k}", file=sys.stderr) + raise ValueError(a) FF = {1: "B", 2: "H", 4:"I"} if a is None: cc, nn, dd = self.cmd(b"M=") @@ -239,10 +223,7 @@ class dose_cmd(uart.uart): def flash(self, op=None, resp=False, poll=False, **aa): c = "F" if poll: - if poll in ".,!": - c = "F"+poll - else: - c = "F!" + c = "F@" if resp: c += "<" if op is None: @@ -297,8 +278,10 @@ class dose_cmd(uart.uart): if min(d) == 255: print(f"FLASH: page is erased") elif bch.check_page(d): - d = self.fix_page(d) - return d + if not bch.galois: + raise bch.BCHError("galois module not loaded for Parity Error correction") + d = bch.fix_page(d) + return d[:512] def read_flash(self, page): print(f"FLASH: reading from {page=}") @@ -372,17 +355,6 @@ class dose_cmd(uart.uart): self.flash(op, size=s, what=what, byte=i) self.wait_for_spi() - def make_parity(self, page): - for i in range(32): - if i==0: - c = "B4!%@" - elif i!=31: - c = "B4!%" - else: - c = "B4!%!" - self.cmd(c, page[16*i:16*(i+1)]) - return self.cmd("B4= 2: + if self._verbose: print(f"FPGA cmd status {s!r}", file=sys.stderr) return d @@ -489,7 +462,7 @@ class dose_cmd(uart.uart): w.extend([0x8001]*(r-1)) w.append(0) d = struct.pack(f">{len(w)}H", *w) - if self._verbose >= 2: + if self._verbose: if mes: mes = f"[{mes}]" else: @@ -497,13 +470,13 @@ class dose_cmd(uart.uart): print(f"icmd{mes} → {[f"{ww:04x}" for ww in w]}") d = self.fpga_cmd(d) r = struct.unpack(f">{len(w)+1}H", d[:2*len(w)+2]) - if self._verbose >= 2: + if self._verbose: print(f" {mes} ← {[f"{rr:04x}" for rr in r]}", file=sys.stderr) return r def acmd(self, c, v=None, task=None, verb=True): _v = self._verbose - self._verbose = verb+1 + self._verbose = _v r = self.icmd(c, v, mes=task)[-1] self._verbose = _v return r @@ -527,7 +500,7 @@ class dose_cmd(uart.uart): nnn -= nn d += dd[2:] r = struct.unpack(f">{len(d)//2}H", d)[:n] - if self._verbose >= 2: + if self._verbose: print(f"FIFO[{fifo}] ← {[f"{rr:04x}" for rr in r]}", file=sys.stderr) return r @@ -549,140 +522,9 @@ class dose_cmd(uart.uart): else: self.icmd("MCONF_CLR", what) - PIPE = { - "CMD" : 1, - "ADC" : 2, - "FLASH" : 4, - "FPGA" : 8, - "CONFIG" : 0x208, - "FPGA_CMD" : 0x408, - } - - def pipe(self, source=None, dest=None, - fpga_cmd=4, psize=64, n=0, - page=0, npages=0, - poll=True, stop=False): - astatus = flags2int(self.PIPE, dest) - astatus |= flags2int(self.PIPE, source) - astatus >>= 8 - dest = flags2int(self.PIPE, dest) & 0xff - source = flags2int(self.PIPE, source) & 0xff - flash = 0x80 # FS_528 - status = 0x80 # PS_OUT - c = 'P' - if astatus & self.PIPE["FPGA_CMD"]: - if not isinstance(fpga_cmd, int): - self.cmd("B4!", fpga_cmd) - fpga_cmd = 4 - c = 'P{fpga_cmd}' - if stop: - c += '@' - if poll: - c += '!' - - if source & self.PIPE["FLASH"]: - source = self.PIPE["FLASH"] - flash |= 4 - status |= 0x30 # PS_528 | PS_BCH - dest &=~ self.PIPE["FLASH"] - if dest & self.PIPE["FPGA"] and not n: - n = 512/64 * npages - if dest & self.PIPE["FLASH"]: - flash |= 8 - status |= 0x30 # PS_528 | PS_BCH - if source == self.PIPE["FPGA"] and not n: - n = (512*npages+psize-1) // psize - - d = struct.pack("<6BH3B2HB", - source, dest, status, 0, 0, - astatus, n, psize, 0, 0, - page, npages-1, flash) - return self.cmd(c, d) - - Sync = False - - def read_pipe(self, n=0, file=None, timeout=2, parity=False): - if isinstance(file, str): - f = open(file, "ab") - else: - f = file - result = [] - i = 0 - page = [] - t = time.time() - s = 1/256 - while i < n: - cc, nn, dd = self.cmd("B") - if not parity or len(page) < 7: - bflg = 0x0f - else: - bflg = 0x1f - - tt = time.time() - if ~nn & bflg: - if tt > t+timeout: - if self._verbose: - print("read_pipe timeout", file=sys.stderr) - break - time.sleep(s) - s *= 2 - if s > 1: - s = 1 - continue - t = tt - s = 1/256 - - if len(result) >= n: - break - - if nn & 0x10: - p = self.cmd("B4<")[2] - else: - p = None - - data = [self.cmd(f"B{i}<")[2] for i in range(4)] - data = b''.join(data) - page.append(data) - if len(page)==8: - if p: - page.append(p) - page = b''.join(page) - i += 1 - if parity: - if bch.check_page(page): - print(f"parity error on page {i}", file=sys.stderr) - if parity == "fix": - page = self.fix_page(page) - if f and parity == "fix": - file.write(page[:512]) - if not f: - result.append(page) - page = [] - - if f and parity != "fix": - f.write(data) - if len(page) and parity == "save": - f.write_flash(p) - if f and self.Sync: - f.sync() - - if isinstance(file, str): - f.close() - return result - - def fix_page(self, page): - if self.load_galois(): - page = bch.fix_page(page) - - def load_galois(self): - if not bch.galois: - raise bch.BCHError("galois module not loaded for Parity Error correction") - return True def flags2int(FLAGS, flags): r = 0 - if flags is None: - return r if isinstance(flags, int): return flags if isinstance(flags, str): @@ -704,7 +546,7 @@ def int2flags(FLAGS, flags): if tty: tty = dose_cmd(tty, baud) tty._export(globals()) - tty._verbose = True + tty._verbose = False import dorn from dorn import * dorn._connect(tty) diff --git a/src/flash.c b/src/flash.c index c50b172..2739ce0 100644 --- a/src/flash.c +++ b/src/flash.c @@ -150,7 +150,7 @@ uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte) spi_start_read(csize, flash_cmd_buffer, size, b); break; case FM_WAIT: - spi.mask = spi.wait = 0x80; + spi.mask = 0x80; spi_start_read(csize, flash_cmd_buffer, 2, b); break; } @@ -173,7 +173,7 @@ uint8_t flash_stream_submit(uint16_t mode, uint8_t size) { uint8_t b = fs.block; uint16_t p = fs.page; - uint8_t r = fs.status & ~FS_Ready | FS_Busy; + uint8_t r = fs.status & ~FS_Ready; mode |= (uint16_t)config.flash_page_size << 8; if (size) { if (b & 8) { @@ -193,9 +193,21 @@ uint8_t flash_stream_submit(uint16_t mode, uint8_t size) uint8_t e = flash_cmd(mode, size, p, (uint16_t)(b&7) << 6); if (e) - r |= FS_Error; + r |= FS_Ready; // FS_Error + r |= FS_Busy; fs.status = r; - return r; + return e; +} + +__attribute__ ((noinline, noclone)) +uint8_t flash_stream_done() +{ + uint8_t r = fs.status & FS_Error; + if (!r || r == FS_Error) + return 1; + if (fs.npages || !(fs.block & 8)) + return 0; + return 1; } static inline @@ -241,50 +253,55 @@ uint8_t flash_burn_page() uint8_t flash_poll(uint8_t rr) { uint8_t r = fs.status; - if (rr) - r |= FS_Ack; if (spi_busy_p()) return r; - if (flash_stream_done()) + if ((r & FS_Error) == FS_Error) return r; - if (r & FS_Ready) - goto ready; - - if (r & FS_StBsy && flash_status_bytes[0] & 0x80) - // flash is done burning + if (r & FS_StBsy) { + // status bytes arrived + if (~flash_status_bytes[0] & 0x80) + // flash is still busy burning + goto rd_status; + // not busy any more, move Bsy → Rdy + if (r & FS_Busy) + r |= FS_Ready; goto ready; - if (r & FS_Write && fs.block == 9) { - // Write or Erase - r |= FS_StBsy; - flash_status_bytes[0] = 0; - flash_cmd_na(0xd7 | FM_READ, 0xf002); - goto done; } - if (r & FS_Dir == FS_Write && fs.block == 8) { - // Write - r = flash_burn_page(); - goto done; + if (!(r & FS_Busy)) + goto ready; + if (rr) { + r |= FS_Error; + fs.status = r; + return r; + } + if (r & FS_Write) { + if (fs.block == 9) { + rd_status: + // request status bytes for pending Write or Error + r |= FS_StBsy; + fs.status = r; + flash_cmd_na(0xd7 | FM_READ, 0xf002); + return r; + } } ready: - r &= ~(FS_Error | FS_StBsy); - if (!(r & FS_Ack)) { - r |= FS_Ready; - goto done; - } - r &=~ FS_Ack; - if (!fs.npages && fs.block & 8) - goto done; - - if ((r & FS_Dir) == FS_Read) - r = flash_read_next_block(); - else if ((r & FS_Dir) == FS_Write) - r = flash_write_next_block(); - else if ((r & FS_Dir) == FS_Erase) - r = flash_erase_next_page(); -done: + r &= ~(FS_Busy | FS_StBsy); + if (rr) + r |= FS_Ack; fs.status = r; - return r; + if (r & FS_Dir == FS_Write && fs.block == 8) + flash_burn_page(); + else if (!flash_stream_done()) { + if (r & (FS_Dir|FS_Ack) == (FS_Read|FS_Ack)) + flash_read_next_block(); + else if (r & (FS_Dir|FS_Ack) == (FS_Write|FS_Ack)) + flash_write_next_block(); + else if (r & FS_Dir == FS_Erase) + flash_erase_next_page(); + fs.status &=~ FS_Ack; + } + return fs.status; } uint8_t flash_start_stream(uint16_t page, uint16_t npages, uint8_t flags) @@ -293,12 +310,14 @@ uint8_t flash_start_stream(uint16_t page, uint16_t npages, uint8_t flags) if ((r & FS_Error) == FS_Busy) return FS_Error; r = flags | FS_Ready; + if (config.flash_page_size != FM_528) + r &=~ FS_528; fs.page = page; - fs.npages = npages; - flash_status_bytes[0] = 0; fs.block = 0; + fs.npages = npages; fs.status = r; - return r; + flash_status_bytes[0] = 0xff; + return flash_poll(0); } static inline diff --git a/src/flash.h b/src/flash.h index 525c493..46af975 100644 --- a/src/flash.h +++ b/src/flash.h @@ -28,6 +28,7 @@ uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte); extern uint8_t flash_buffer[FB_SIZE]; uint8_t flash_submit_command(uint8_t *cmd); uint8_t flash_start_stream(uint16_t page, uint16_t npages, uint8_t flags); +uint8_t flash_stream_done(); uint8_t flash_poll(uint8_t rr); uint16_t flash_find_free(); @@ -54,7 +55,5 @@ enum { }; static inline uint8_t flash_current_block() { return fs.block; } -static inline uint8_t flash_stream_done() -{ return !(fs.status & FS_Error) || !(~fs.status & FS_Error); } #endif diff --git a/src/fpga.c b/src/fpga.c index fc01a79..9300742 100644 --- a/src/fpga.c +++ b/src/fpga.c @@ -113,6 +113,7 @@ uint8_t fpga_start_read() uint8_t n = 64 - pipe.fpga.val; if (!n) return n; + spi.rdata = flash_buffer + pipe.fpga.val; n >>= 1; if (~pipe.fpga.status & AS_CONT) { if (!pipe.fpga.count) @@ -129,10 +130,7 @@ uint8_t fpga_start_read() pipe.fpga.pos += n; if (pipe.fpga.pos == pipe.fpga.size) pipe.fpga.status &=~ AS_CONT; - - spi.rdata = flash_buffer + pipe.fpga.val; spi.rsize = n << 1; - spi.zsize += spi.rsize; pipe.fpga.val += spi.rsize; spi_start(); return n; diff --git a/src/pipe.c b/src/pipe.c index 2ee6058..edd4c60 100644 --- a/src/pipe.c +++ b/src/pipe.c @@ -18,7 +18,7 @@ uint8_t pipe_busy() // call this with cli() before sei();sleep() if (spi_busy_p()) return 1; if (flash_poll(0) & FS_Busy) return 1; - if (adc_busy()) return 1; + if (adc_poll(0)) return 1; return 0; } @@ -77,14 +77,14 @@ uint8_t pipe_poll() // Return if next ADC reading is not yet due. // Come back here, until is is. - if (pipe.source == pipe_adc && !adc_poll(pipe.adc)) + if (pipe.source & pipe_adc && !adc_poll(pipe.adc)) goto done; // Continue the flash stream. // PS_BLK is the 64-Bytes buffer number in the page valid = 0; - if (pipe.source == pipe_flash) { + if (pipe.source & pipe_flash) { r &= ~ PS_BLK; r |= flash_current_block() & PS_BLK; flash_poll(1); @@ -92,9 +92,9 @@ uint8_t pipe_poll() #ifdef HAVE_FPGA // Continue the FPGA stream. - else if (pipe.source == pipe_fpga) { + else if (pipe.source & pipe_fpga) { if (~fpga_start_read()) - pipe.source = 0; + pipe.source &=~ pipe_fpga; } #endif goto done; @@ -109,7 +109,7 @@ uint8_t pipe_poll() bflgs = 0x1f; // Data from the ADC is in named .bss segments. We send - // 16, 32, or 64 Bytes from the .bss segement for each ADC reading. + // 16, 32, or 64 Bytes from the .bss segement for ead ADC reading. // The first named .bss segment is `magic`. Observe the link order // in the Makefile. // @@ -118,7 +118,7 @@ uint8_t pipe_poll() // `o` is the addr in the flash_buffer // `v` are the currently valid cmd_buffer flags // `f` are the fresh valid cmd_buffer flags - if (pipe.source == pipe_adc) { + if (pipe.source & pipe_adc) { uint8_t n = pipe.adc & 0x70; if (!n) goto adc_done; @@ -160,11 +160,11 @@ adc_done: // When the flash is done, flag the buffer valid, // including BCH Bytes. - if (pipe.source == pipe_flash && fs.status & FS_Ready) + if (pipe.source & pipe_flash && fs.status & FS_Ready) valid = bflgs; #ifdef HAVE_FPGA // The FPGA delivers 64 Bytes. - else if (pipe.source == pipe_fpga && fpga_pipe_ready()) + else if (pipe.source & pipe_fpga && fpga_pipe_ready()) valid = 0x0f; #endif // The buffer is not here, yet. Nothing we can do now. @@ -181,10 +181,10 @@ adc_done: // Last buffer of the page: if (!(~r & PS_BLK)) { bch4369_fini(); + valid = 0x01f; // When the Flash is not the source, - // and cmd did not provide the parity // copy the computed parity into the buffer. - if (~valid & 0x10) + if (~pipe.source & pipe_flash) _memcopyyz(bend, bch_parity, 16); // When the page as read from flash has invalid parity, // stop writing to the FPGA. @@ -192,7 +192,6 @@ adc_done: dest &=~ pipe_fpga; r |= PS_ERR; } - valid = 0x1f; } } // We still do not have all data we need @@ -204,7 +203,7 @@ adc_done: #ifdef HAVE_FPGA // Resume the FPGA stream - if (dest & pipe_fpga && ~fpga_start_write()) + if (dest & pipe_fpga & ~fpga_start_write()) dest &=~ pipe_fpga; else #endif @@ -228,7 +227,7 @@ void pipe_config(const struct pipe_config *c, const struct pipe_fpga_cmd *a) { /************************************************************ * cmd("B0!", «fpga-cmd») optionally configure an FPGA cmd - * cmd("P0", «pipe-cfg») with `AS_CMD` in `.status` + * cmd("P0:, «pipe-cfg») with `AS_CMD` in `.status` ***********************************************************/ if (c->pipe.dest) { @@ -239,7 +238,6 @@ void pipe_config(const struct pipe_config *c, const struct pipe_fpga_cmd *a) else { // new source (NOT flash) pipe.source = c->pipe.source; - pipe.adc = c->pipe.adc; } if (pipe.source & pipe_adc) adc_start_stream(pipe.adc); @@ -249,7 +247,6 @@ void pipe_config(const struct pipe_config *c, const struct pipe_fpga_cmd *a) _memcopyzy((void*)&pipe_fpga_cmd, (void*)a, sizeof(*a)); else memset(&pipe_fpga_cmd, 0, sizeof(*a)); - pipe.fpga = c->pipe.fpga; fpga_start_read(); } #endif diff --git a/src/spi.c b/src/spi.c index 083ab8d..98a4329 100644 --- a/src/spi.c +++ b/src/spi.c @@ -387,18 +387,23 @@ ISR(SPI0_INT_vect, ISR_NAKED) "rjmp 4f" "\n" "1:" "\n\t" - "lds r26, %[WAIT]" "\n\t" - "eor r26, r25" "\n\t" - "lds r24, %[MASK]" "\n\t" - "and r26, r24" "\n\t" - "breq 2f" "\n\t" + "lds r26, %[MASK]" "\n\t" + "cpi r26, 0" "\n\t" + "breq 1f" "\n\t" + "and r26, r25" "\n\t" + "lds r24, %[WAIT]" "\n\t" + "cp r24, r26" "\n\t" + "brne 2f" "\n\t" "lds r25, %[ZSZ]" "\n\t" "subi r25, -1" "\n\t" "sts %[ZSZ], r25" "\n\t" "rjmp 4f" "\n" "2:" "\n\t" - "sts %[MASK], r26" "\n\t" + "clr r26" "\n\t" + "sts %[MASK], r26" "\n" + + "1:" "\n\t" "lds r26, %[RSZ]" "\n\t" "subi r26, 1" "\n\t" "brcs 4f" "\n\t" diff --git a/src/thhor.c b/src/thhor.c index 8d3ebc6..e6a2869 100644 --- a/src/thhor.c +++ b/src/thhor.c @@ -13,7 +13,6 @@ #include "config.h" #include "uart.h" #include "pipe.h" -#include "adc.h" //////////////////////////////////////////////////////////////////////////////// // @@ -41,18 +40,17 @@ int main() send_hex_byte_eol(magic.reset_source); while (1) { - // The irq sources may become idle before we reach sleep. - // Make sure we have nothing pending before we sleep. - // The sleep command will execute even when an irq becomes - // pending after cli(). It will wake us immediately. + // The pipe may become idle before we reach sleep. + // Make sure we have nothing to do before we sleep. + // The sleep command will execute even when an irq + // is pending. It will wake us immediately. cli(); - if (!command_pending() || adc_busy() || spi_busy_p()) { + if (!command_pending() && (!pipe.dest || pipe_busy())) { sei(); sleep_cpu(); } sei(); command(); - if (~magic.flags & hold_pipe) - pipe_poll(); + pipe_poll(); } } diff --git a/src/uart.py b/src/uart.py index 4ba9779..e8bdba3 100755 --- a/src/uart.py +++ b/src/uart.py @@ -61,7 +61,7 @@ class uart(threading.Thread): def parse_line(self, l): self.responses.append(l) - if self._verbose >= 4: + if self._verbose: try: s = l.decode() print(f"{self.portname}<- {s}", file=sys.stderr) @@ -92,7 +92,7 @@ class uart(threading.Thread): c = c.encode() if c[-1:] != b'\n': c = c + b'\n' - if self._verbose >= 4: + if self._verbose: print(f"{self.portname}-> {c.decode().rstrip()}", file=sys.stderr) self.write(c)