Information wants to be free...

CP/M-68K for Motorola 68332

I have created a CP/M BIOS to be able to run CP/M-68K on an embedded system with a Motorola 68332 MCU. The 68332 contains a "Queued Serial Module" that the BIOS interacts with to provide a console for CP/M over a RS-232 serial port. There is only support for one disk, which is a RAM disk implementation, also handled by the BIOS. During development I relied heavily on the BDM functionality which I made an adapter for, but this can also be used to load the CP/M system and RAM disk image directly into memory through the use of S-records.

Since CP/M-68K programs are written for pure Motorola 68000 CPUs there is a known incompatibility with one particular "MOVE SR,xx" instruction in Motorola 68010 and later CPUs including the 68332 core. It is possible to patch this in software, so this BIOS implements the DeciGEL method originally used by some upgraded Amiga systems.

The particular board I am using has 256KB of RAM available, mapped starting at 0x100000 and I ended up with the following memory map by dividing it into 16x16KB parts:

|--------|--------|----------|
| Start  | End    | Use      |
|--------|--------|----------|
| 100000 | 103FFF | VBR      |
| 104000 | 107FFF | TPA      |
| 108000 | 10BFFF | TPA      |
| 10C000 | 10FFFF | TPA      |
| 110000 | 113FFF | TPA      |
| 114000 | 117FFF | RAM-Disk |
| 118000 | 11BFFF | RAM-Disk |
| 11C000 | 11FFFF | RAM-Disk |
| 120000 | 123FFF | RAM-Disk |
| 124000 | 127FFF | RAM-Disk |
| 128000 | 12BFFF | RAM-Disk |
| 12C000 | 12FFFF | RAM-Disk |
| 130000 | 133FFF | RAM-Disk |
| 134000 | 137FFF | RAM-Disk |
| 138000 | 13BFFF | CP/M     |
| 13C000 | 13FFFF | CP/M     |
|--------|--------|----------|
          

This board already has it's own BIOS that sets up the hardware (e.g. the UART in the Queued Serial Module) and ends up pointing the CPU Vector Base Register (VBR) to 0x100000. TPA is the "Transient Program Area" used by programs that run within CP/M.

The RAM disk image can be prepared on another Linux computer using cpmtools and the following definition:

diskdef ram-68332
  seclen 128
  tracks 72
  sectrk 16
  blocksize 1024
  maxdir 64
  skew 0
  boottrk 0
  os 2.2
end
          


The commands involved in preparing the disk and then a S-record representation would typically be:

mkfs.cpm -f ram-68332 ramdisk.bin
cpmcp -f ram-68332 ramdisk.bin <file> 0:<file>
srec_cat ramdisk.bin -binary -offset 0x114000 -o ramdisk.s68 -motorola -disable=data-count -execution_start_address 0
          


Assembling the BIOS and linking CP/M-68K for a new system requires another already running CP/M-68K system with the development tools. The Digital Research documentation from the 80's suggests using a Motorola EXORmacs development system, but luckily we have emulators available for this purpose now.

From within the emulator the following steps are used to assemble, link and relocate the CP/M-68K system binary. The final command dumps the binary in S-record format to the console:

AS68 BIOS332.S
LO68 -R -UCPM -O CPM.REL CPMLIB BIOS332.O
RELOC -B138000 CPM.REL CPM.SYS
SENDC68 CPM.SYS
          

The CPMLIB library I got from the CP/M-68K version 1.3 disk set here.

Here is the BIOS assembly source code (BIOS332.S) in 68000 assembly language, it is based on the "ERG" BIOS from the Digital Research manuals:

*****************************************************************
*                                                               *
*               CP/M-68K BIOS                                   *
*       Basic Input/Output Subsystem                            *
*       For Motorola 68332 Embedded System                      *
*                                                               *
*****************************************************************

        .globl  _init           * BIOS initialization entry point
        .globl  _ccp            * CCP entry point

_init:  move.l  #traphndl,$10008c * Set up trap #3 handler (on VBR offset)
        move.l  #privhndl,$100020 * Catch privilege exception (on VBR offset)
        move.l  #welcome,a0     * Display welcome message
weloop: move.b  (a0)+,d1
        cmpi.b  #$24,d1         * Compare against '$'
        beq     wedone
        jsr     conout
        bra     weloop
wedone: clr.l   d0              * Log on disk A:, user 0
        rts

traphndl:
        cmpi    #nfuncs,d0
        bcc     trapng
        lsl     #2,d0           * Multiply bios function by 4
        movea.l 6(pc,d0),a0     * Get handler address
        jsr     (a0)            * Call handler
trapng:
        rte

biosbase:
        .dc.l  _init    *  0 - Initialization
        .dc.l  wboot    *  1 - Warm boot
        .dc.l  constat  *  2 - Console status
        .dc.l  conin    *  3 - Read console character
        .dc.l  conout   *  4 - Write console character
        .dc.l  lstout   *  5 - List character output
        .dc.l  pun      *  6 - Auxiliary output
        .dc.l  rdr      *  7 - Auxiliary input
        .dc.l  home     *  8 - Home
        .dc.l  seldsk   *  9 - Select disk drive
        .dc.l  settrk   * 10 - Select track number
        .dc.l  setsec   * 11 - Select sector number
        .dc.l  setdma   * 12 - Set DMA address
        .dc.l  read     * 13 - Read sector
        .dc.l  write    * 14 - Write sector
        .dc.l  listst   * 15 - Return list status
        .dc.l  sectran  * 16 - Sector translate
        .dc.l  home     * 17 - N/A
        .dc.l  getseg   * 18 - Get address of memory region table
        .dc.l  getiob   * 19 - Get I/O byte
        .dc.l  setiob   * 20 - Set I/O byte
        .dc.l  flush    * 21 - Flush buffers
        .dc.l  setexc   * 22 - Set exception handle address

        nfuncs=(*-biosbase)/4

wboot:  jmp     _ccp

constat: move.b $fffc0d,d0      * Get status from SCSR register
        andi.b  #$40,d0         * Check for RDRF=1 data available?
        beq     noton           * Branch if not
        moveq.l #$1,d0          * Set result to true
        rts
noton:  clr.l   d0              * Set result to false
        rts

conin:  bsr     constat         * See if key pressed
        tst     d0
        beq     conin           * Wait until key pressed
        move.b  $fffc0f,d0      * Get key from SCDR register
        rts

conout: move.b  $fffc0c,d0      * Get status from SCSR register
        andi.b  #$01,d0         * Check for TDRE=1 transmit OK?
        beq     conout          * Wait until our port has aged...
        move.b  d1,$fffc0f      * And output it to SCDR register
        rts

lstout: rts

pun:    rts

rdr:    rts

listst: move.b  #$ff,d0 * Device not ready
        rts

home:   rts

seldsk:
        moveq   #0,d0
        cmpi.b  #1,d1           * Only disk A: RAM disk supported
        bpl     selrtn          * Return 0 in d0 for other disks
        move.l  #$114000,selmem * Prepare base memory address for RAM disk
        move.l  #dph,d0         * Point d0 at dph
selrtn: rts

settrk: move.b  d1,track
        rts

setsec: move.b  d1,sector
        rts

setdma: move.l  d1,dma
        rts

sectran: move.w d1,d0   * No sector translation, just 1-to-1 mapping
        rts

read:
        clr.l   d0
        move.b  track,d0
        mulu    #16,d0          * Multiply by SPT
        add.b   sector,d0
        mulu    #128,d0         * Multiply by sector size
        add.l   selmem,d0       * Add offset for RAM disk address in memory
        move.l  dma,a0
        move.l  d0,a1
        move.l  #127,d0         * Read 128 bytes
rloop:  move.b  (a1)+,(a0)+     * Transfer byte
        dbra    d0,rloop
        clr.l   d0
        rts

write:
        clr.l   d0
        move.b  track,d0
        mulu    #16,d0          * Multiply by SPT
        add.b   sector,d0
        mulu    #128,d0         * Multiply by sector size
        add.l   selmem,d0       * Add offset for RAM disk address in memory
        move.l  dma,a0
        move.l  d0,a1
        move.l  #127,d0         * Write 128 bytes
wloop:  move.b  (a0)+,(a1)+     * Transfer byte
        dbra    d0,wloop
        clr.l   d0
        rts

flush:  clr.l   d0      * Return successful
        rts

getseg: move.l  #memrgn,d0
        rts

getiob: rts

setiob: rts

setexc: andi.l  #$ff,d1         * Do only for exceptions 0 - 255
        cmpi    #8,d1
        beq     noset           * Skip touch privilege exception
        cmpi    #9,d1           * and Trace exception
        beq     noset
        lsl     #2,d1           * Multiply exception number by 4
        movea.l d1,a0
        add.l   #$100000,a0     * Add VBR to address
        move.l  (a0),d0         * Return old vector value
        move.l  d2,(a0)         * Insert new vector
noset:  rts

* DeciGEL patch originally made for Amiga systems using Motorola 68010
privhndl:
        movem.l D0/A0,-(SP)     * Save registers
        move.l  8+2(SP),A0      * Pointer to opcode
        move.w  (A0),D0         * Pickup opcode
        andi.w  #$FFC0,D0       * Mask out EA field
        cmpi.w  #$40C0,D0       * Is it a MOVE SR,ea?
        bne.s   privsk
        bset    #1,(A0)         * Convert it to MOVE CCR,ea
privsk: movem.l (SP)+,D0/A0     * Restore regs
        rte                     * Rerun new opcode



        .data

* Welcome text

welcome: .dc.b  13,10,'CP/M for Motorola 68332',13,10,'$'

* Disk variables

selmem: .dc.l   0       * Disk (RAM disk base address) requested by seldsk
track:  .dc.b   0       * Track requested by settrk
sector: .dc.b   0       * Sector requested by setsec
dma:    .dc.l   0       * DMA address requested by setdma

* Memory region definition

memrgn: .dc.w   1       * 1 memory region
        .dc.l   $104000 * Start
        .dc.l   $ffff   * Length/size

* Disk parameter header

dph:    .dc.l   0       * No translation table used
        .dc.w   0       * Dummy
        .dc.w   0
        .dc.w   0
        .dc.l   dirbuf  * Pointer to directory buffer
        .dc.l   dpb     * Pointer to disk parameter block
        .dc.l   0       * Check vector not used for unremovable RAM disk
        .dc.l   alv     * Pointer to allocation vector

* Disk parameter block

dpb:    .dc.w   16      * Sectors per track
        .dc.b   3       * Block shift
        .dc.b   7       * Block mask
        .dc.b   0       * Extent mask
        .dc.b   0       * Dummy fill
        .dc.w   143     * Disk size
        .dc.w   63      * 64 directory entries
        .dc.w   0       * Reserved
        .dc.w   0       * Check vector not used for unremovable RAM disk
        .dc.w   0       * Track offset zero since no boot sector on RAM disk



        .bss

dirbuf: .ds.b   128     * Directory buffer
alv:    .ds.b   18      * Allocation vector = (disk size / 8) + 1

        .end
          


Topic: Scripts and Code, by Kjetil @ 20/01-2023, Article Link

Motorola BDM Debugger in DOS QEMU

I have some boards with a Motorola 68332 MCU (which later became Freescale and then NXP) that have a Motorola 68000 core. There is a debugging port on these boards known as "BDM" or "Background Debug Mode" which is very useful.

I have managed to connect to this port using an adapter that can be connected to a standard PC parallel port. There seems to be two versions of this adapter found on the Internet, one using a 74LS74 flip-flop and another using a 74LS76 flip-flop.

I built the 74LS74 variant using a prototype circuit board. To help with this I converted the schematic to another form to show where to connect all the wires using color coding:

BDM Board Schematic


Here is a picture of the finished adapter without the logic chips. 14-pin DIP sockets should have been used but I only had 20-pin DIP sockets available:

BDM Board


Here are the signals on the pin header that can be connected to a parallel port on a PC:

|-------|------------|--------|
| DB-25 | Signal     | Header |
|-------|------------|--------|
| 17    | STEP_OUT   | A      |
| 10    | PWR_DN/VCC | B      |
| 14    | RSTOUT     | C      |
|  1    | DSCLK      | D      |
| 15    | FREEZE     | E      |
| 12    | DSO        | F      |
| 16    | DSI        | H      |
| 11+18 | NC/GND     | I      |
|-------|------------|--------|
          


Here are the signals going to the target BDM port:

|---------|--------|
| Signal  | Header |
|---------|--------|
| DSCLK   |  4     |
| VSS/GND |  5     |
| FREEZE  |  6     |
| RESET   |  7     |
| DSI     |  8     |
| VDD/VCC |  9     |
| DSO     | 10     |
|---------|--------|
          


I have put MS-DOS 6.22 on a QEMU x86 emulator image and then installed the DOS-based "BD32" program from Motorola which can still be found on NXP's pages.

The modern PC I am using has a PCI-express board with a parallel port and enabling passthrough in the QEMU emulator on Linux done by simply passing this on the command line arguments:

-parallel /dev/parport0
          


It is important to "slow down" the communication speed in BD32 when running on faster hardware, so I have used the setting "1000" which can be configured in the "bd32.cfg" file like so:

lpt1
1000
          


Here is a screenshot of QEMU running BD32 in DOS:

BD32 in QEMU


Topic: Configuration, by Kjetil @ 06/01-2023, Article Link

SPEA Graphiti Painter 3

I have a 2D graphics card from the early 90s typically used for CAD work, manufactured by a now defunct German company named SPEA. The card is the "Graphiti Painter 3". Instead of the regular HD15 VGA connector it uses a DE9 connector.

To be able to connect it to a regular VGA monitor, I made an adapter using the following connection table:

|-------------|-----|-----|
|             | VGA | DE9 |
| Signal      | Pin | Pin |
|-------------|-----|-----|
| Red Video   | 1   | 1   |
| Green Video | 2   | 2   |
| Blue Video  | 3   | 3   |
| Ground      | 5   | 9   |
| HSync       | 13  | 4   |
| VSync       | 14  | 5   |
|-------------|-----|-----|
          


I happened to have both connectors available on those cables typically connected to motherboards or SBCs. These can easily be linked together using male pin-header wires. In addition I added a ferrite core which may help with noise:

HD15 to DE9 converter cable.


The only drivers available for the card is for Windows 3.x or certain CAD programs, but here are some graphics generated by the "GDCLOGO.EXE" program run from DOS:

Logo generated by SPEA software.


Topic: Configuration, by Kjetil @ 16/12-2022, Article Link

PS/2 Floppy Drive Capacitor Replacement

I was able to successfully change the SMD electrolytic capacitors on a PS/2 floppy drive used in the P70/P75 luggable computers. These capacitors are prone to leaking and may eventually cause damage to the circuit boards. The floppy drive manufacturer is ALPS and the model number is DFP723D15C. It is also marked with IBM numbers "P/N 79F3224" and "FRU P/N 79F3225". Since PS/2 floppy drives have a slightly different connector they cannot be easily replaced with a standard PC floppy drive.

Here is the list of electrolytic capacitors:

|----------------|-----|-----------------------|
|Location        | ID  | Value                 |
|----------------|-----|-----------------------|
|Interface Board | C3  | 10uF 16V              |
|Interface Board | C4  | 10uF 16V              |
|Interface Board | C5  | 10uF 16V              |
|Interface Board | C6  | 10uF 16V              |
|Interface Board | C7  | 10uF 16V              |
|----------------|-----|-----------------------|
|Motor Board     | 6C  | 10uF 16V              |
|Motor Board     | 8C  | 4.7uF 16V Unpolarized |
|Motor Board     | 9C  | 4.7uF 16V Unpolarized |
|Motor Board     | 10C | 4.7uF 16V Unpolarized |
|Motor Board     | 7C  | 4.7uF 25V             |
|----------------|-----|-----------------------|
          


And here is an overview image:

Capacitor overview


For replacements of the 10uF caps I used 35V rated "RND 150VVT035M100CA1L" and for the 4.7uF polarized cap a 50V rated "RND 150VVT050M4R7CA1L". I did not find any unpolarized SMD type electrolytic caps so I used ceramic caps "Murata GRJ32DC72A475KE11L" instead.

Here are some images of the replacements:

New capactors spot #1

New capactors spot #2

New capactors spot #3


It is also worth mentioning that I had to lubricate the floppy drive as well, since it seemed to have trouble moving when I tested. I used white lithium grease on the stepper motor drive shaft and silicone grease on the plastic surfaces where the head assembly slide back and forth.

Topic: Repair, by Kjetil @ 25/11-2022, Article Link

lazyboNES Emulator TAS Support

I have made some updates to the lazyboNES emulator which is used to run Super Mario Bros in a Linux terminal.

Some updates were made a while ago actually, in the GitHub repository, tagged as version 0.2. The main feature then was support for 8 ANSI colors, as well as various bugfixes and minor improvements that was found during play testing.

Now I present version 0.3. (An update has also been made to the GitHub repo.) The main feature is support for Tool-assisted speedruns by reading controller inputs from a FM2 file.

Some critical changes were needed in order to get this to work properly, if not the controller input would not be correctly aligned with the video frames. The first requirement was better synchronization between the CPU and the PPU (video) where the clock cycles for each needed to be more accurate and similar to a real NES. The second requirement was that the PPU needs to run a few frames by itself before the CPU is started.

Here is a screenshot of the color mode:

lazyboNES color screenshot


For a full TAS playthrough in lazyboNES check this YouTube video.

Topic: Open Source, by Kjetil @ 28/10-2022, Article Link

More CP/M Emulator Ports

I decided to port my "kaytil" Z80 CP/M 2.2 emulator to even more platforms to see if it could be done. It can now be compiled for DOS by using the DJGPP toolchain or Windows by using the MinGW toolchain. In order to support Windows, I needed to make a curses based implementation of the console interface and this can also be used to compile an alternative Linux version based on (n)curses instead.

Since DJGPP is actually a 32-bit environment the DOS version also requires a 32-bit CPU to run and will use the CWSDPMI.EXE extender. The CPU needs to be fast as well, at least a Pentium II, to get acceptable speeds. But in order to not run too fast, a slowdown mechanism exists in the emulator as well. This slowdown mechanism is just a busy-wait since DOS has no multitasking concept and will just keeps the entire CPU to itself anyway.

I ran a simple benchmark for the DOS version by using "BASIC-80 Rev. 5.21" and the following BASIC program:

10 FOR I=0 TO 10000 STEP 1
20 PRINT I
30 NEXT I
          


Here are the results:

|----------------------|---------------------|
|                      |      Slowdown       |
|System                | Enabled    Disabled |
|----------------------|----------|----------|
|QEMU (AMD Ryzen 9)    | 1m11s    | 0m21s    |
|DOSBox (AMD Ryzen 9)  | 1m11s    | -        |        
|Pentium 166MHz        | 2m01s    | 1m59s    |        
|486SX 25MHz           | 19m16s   | -        |
|----------------------|----------|----------|
          


So it is possible to run it on a 486 CPU but it will be very slow.

The updated source code can be found here or at the Git repo here. Instructions for building is on the README.md within, but for convenience, here is a binary of the DOS version and Windows version.

Topic: Open Source, by Kjetil @ 14/10-2022, Article Link

Sharp IQ-7100M Linux Link

Here is a way to download data from a Sharp IQ-7100M organizer into a modern Linux system. To be able to make the connection you will need to use a USB<->UART converter that operates on a lower TTL voltage level and most importantly INVERTS the TXD and RXD logic levels. I used the SparkFun DEV-09873 which is actually 3.3V, but the 5V version probably works as well since the Sharp organizer uses 5V TTL levels. The FTDI FT232RL chip on this supports inverting the TXD and RXD logic levels by using the "FT_PROG" program to reprogram the settings.

The connection table is as follows:

|------|--------------|
| FTDI | Sharp 15-Pin |
| -----|---|----------|
| GND  | 7 | GND      |
| RXD  | 2 | TXD      |
| TXD  | 3 | RXD      |
|------|---|----------|
          


With the SparkFun adapter it is possible to just use three wires with male Dupont connectors:

Sharp IQ-7100M


Here is a small C program that will download the "MEMO" database:

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

#define TTY_DEVICE "/dev/ttyUSB0"

static const char command[] = "\x04R030000\r\nMEMO    1  \r\n";

int main(void)
{
  int fd;
  struct termios tios;
  unsigned char byte;
  const char *p;

  fd = open(TTY_DEVICE, O_RDWR | O_NOCTTY);
  if (fd == -1) {
    fprintf(stderr, "open() failed with errno: %d\n", errno);
    return EXIT_FAILURE;
  }

  if (tcgetattr(fd, &tios) == -1) {
    fprintf(stderr, "tcgetattr() failed with errno: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }

  cfmakeraw(&tios);
  cfsetispeed(&tios, B9600);
  cfsetospeed(&tios, B9600);

  if (tcsetattr(fd, TCSANOW, &tios) == -1) {
    fprintf(stderr, "tcsetattr() failed with errno: %d\n", errno);
    close(fd);
    return EXIT_FAILURE;
  }

  /* Send the command, using a small delay: */
  p = &command[0];
  while (*p != '\0') {
    if (write(fd, p, 1) == -1) {
      fprintf(stderr, "write() failed with errno: %d\n", errno);
      close(fd);
      return EXIT_FAILURE;
    }
    usleep(10000);
    p++;
  }

  /* Read the response until a EOF character appears: */
  do {
    if (read(fd, &byte, 1) != 1) {
      fprintf(stderr, "read() failed with errno: %d\n", errno);
      close(fd);
      return EXIT_FAILURE;
    }
    putc(byte, stdout);
  } while (byte != 0x1A);

  close(fd);
  return EXIT_SUCCESS;
}
          


This program just dumps the raw data, which is already in ASCII. Since there is no flow control, a sleep of 10ms was added between each byte sent to prevent the Sharp organizer from overflowing and giving up. Note that you will need to be in the "PC-Link" menu for any transfer to work, which is reached by pressing "Shift" + "Option" then "4".

Topic: Scripts and Code, by Kjetil @ 23/09-2022, Article Link

Epson HX-20 Emulator

I have made an emulator called "hex20" that emulates the Epson HX-20 Portable Computer in a Linux terminal, similar to my other Z80 CP/M, NES and C64 emulators.

The HX-20 system uses two Hitachi HD6301 CPUs (based on the Motorola 6800 family) which communicate with each other over a internal serial link. This emulator needs the ROM files for both of these to function. A CRC32 check is performed on startup to make sure that the correct version 1.0 or version 1.1 ROM files are used.

One of the main features is the emulation of the LCD panel by using curses to display each individual pixel as a '#' (or '%') on a large 120x32 terminal window. It is also possible to force a smaller 20x4 window, but then only ASCII will be displayed and none of the special pixel drawing functions will work. Most of the keyboard keys are supported and multiple international character sets can be selected.

Another feature is auto-loading of BASIC program text files. This is achieved through clever use of the HX-20 ROM software ability to do automatic key input. A '2' is injected to enter BASIC from the menu, then all the text from the file is sent, and finally a BASIC RUN command is issued to start execution. On a real HX-20 a "Ctrl/@" initialization is usually needed on a cold start, but all the necessary data is already initialized automatically by the emulator itself. The RTC is also mapped directly to the host system clock so it does not need to be set.

The initial version can be downloaded here but the code is also available in this Git repository for further development. Note that at this point several features are still missing like sound support, TF-20 floppy drive and micro-cassette emulation.

Some screenshots from the emulator running in xterm:

Main menu

Clock

Blackjack

Blimp


Development of the emulator would have been impossible without the excellent Epson HX-20 resources from F. J. Kraan's web pages.

Topic: Open Source, by Kjetil @ 02/09-2022, Article Link

Monospaced 11x10 Font

For my VT100 terminal emulator on Raspberry Pi Pico project I needed to make my own font to fit the specific requirements. For convenience, that source code only contains the font data as a static "char.rom" binary file. For completeness sake I present the font data source here.

The source data is an image, so any modifications can be done in an image editor:

11x10 Font Data


As can be seen here the image is divided into a grid of 16x16 to provide 256 characters in total.

This image needs to be converted to PGM format and then be fed into a special utility that converts it to the actual ROM binary file.

The source code for the PGM to ROM utility is provided here, which operates on stdin/stdout for the conversion:

#include <stdio.h>
#include <stdbool.h>

#define HEIGHT 10
#define WIDTH 11
#define CHAR_PER_LINE 16
#define BYTES_PER_LINE 176
#define COLLECT (HEIGHT * BYTES_PER_LINE)

static unsigned char buffer[COLLECT];

static int power_of_two(int n)
{
  if (n == 0) {
    return 1;
  } else {
    return 2 * power_of_two(n - 1);
  }
}

int main(void)
{
  int skip = 4;
  int c, n, i, j, k;
  int b;

  n = 0;
  while ((c = fgetc(stdin)) != EOF) {
    if (skip > 0) {
      if (c == '\n') {
        skip--;
      }
      continue;
    }

    buffer[n] = c;
    n++;
    if (n >= COLLECT) {
      n = 0;

      /* Store as 16 bits by 10 lines, even if 11 bits wide. */

      for (i = 0; i < CHAR_PER_LINE; i++) {
        for (j = 0; j < HEIGHT; j++) {
          b = 0;
          for (k = 0; k < 8; k++) {
            if (buffer[k + (i * WIDTH) + (j * BYTES_PER_LINE)] == 0) {
              b += power_of_two(7 - k);
            }
          }
          fputc(b, stdout);

          b = 0;
          for (k = 0; k < 3; k++) {
            if (buffer[k + 8 + (i * WIDTH) + (j * BYTES_PER_LINE)] == 0) {
              b += power_of_two(2 - k);
            }
          }
          fputc(b, stdout);
        }
      }
    }
  }

  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 19/08-2022, Article Link

CentreCOM MR820TR Hub Repair

I found a broken "Allied Telesyn CentreCOM MR820TR" network hub in the trash:

MR820TR Broken


When power was applied the power LED would just blink rapidly. The internal PSU is supposed to supply +5V and +12V on two rails, but when I measured these with the logic board connected they were just +1.6V and +8.1V. Disconnecting the logic board showed +4.8V on the +5V rail and variying voltage between +7.3V and +7.6V on the +12V rail, so likely something wrong with the internal PSU.

I measured the ESR of several electrolytic capacitors in-circuit and one of them (C06) in the center of the board had bad readings. I unsoldered this one and measured it again, and it was still bad:

MR820TR Bad Capacitor


I replaced it with a new one:

MR820TR New Capacitor


This network hub is now working again:

MR820TR Fixed


Topic: Repair, by Kjetil @ 05/08-2022, Article Link

Older articles