Information wants to be free...

CDROM Drive Test Report

Here is yet another article on hardware testing. Similar to the one on floppy drives and hard disk drives from earlier. Now for my collection of CDROM (and DVD) drives, to filter out which ones still work and which ones doesn't. I have compiled a complete (LaTeX typesetted) report on the test results here. A total of 14 drives were investigated, where 10 were OK, 3 were untestable and 1 was broken.

There are tests on the following hard drive models in the report:
* Aztech CDA 268-01A
* Aztech CDA 468-02I
* Creative CR-587-B
* Goldstar CRD-8160B
* HP XJ-HD166S
* Matsushita CR-504-B
* Philips CDD3600/51
* Pioneer DR-A12X
* Plextor PX-712A
* Plextor PX-716SA
* Samsung TS-H352
* Sony AD-5260S
* Sony CRX320A
* Toshiba XM-5302B

Collection:

CDROM drive collection


Topic: Mundane, by Kjetil @ 09/06-2019, Article Link

Hard Drive Test Report

Here's another article on old hardware testing, like the one on floppy drives from before. This time I decided to go through my collection of hard drives test those, again to filter out which ones still work and which ones doesn't. Once again, I have compiled a complete (LaTeX typesetted) report on the test results here. A total of 13 drives were investigated, where 10 were OK and 3 were broken.

There are tests on the following hard drive models in the report:
* Quantum ProDrive ELS
* Quantum ProDrive LPS (x2)
* Seagate ST-1144A
* Seagate ST-1239A
* Seagate ST-157A
* Seagate ST-3096A
* Seagate ST-3144A
* Seagate ST-3195A
* Seagate ST-351AX (x2)
* Conner CFA270A
* Conner CP-3104

Collection:

Hard drive collection


Topic: Mundane, by Kjetil @ 01/05-2019, Article Link

Floppy Drive Test Report

I decided to go through my collection of old 3.5" floppy drives and test them all, in order to filter out which ones still work or not. I compiled a complete (LaTeX typesetted) report on the test results here. A total of 20 drives were investigated. In short, 13 were OK, 4 were broken and 3 were untestable.

There are tests on the following floppy drive models in the report:
* Citizen LR102061 A.L.
* JPN ZFDD1.44M
* Mitsubishi MF355C-252M
* NEC FD1138H
* Panasonic JU-256A276P
* Panasonic JU-257-204P
* Panasonic JU-257A604P
* Panasonic JU-257A606P (x2)
* Sony MPF920 (x4)
* Sony Model MPF40W-00
* Sony Model MPF520-1
* TEAC FD-235HF
* TEAC FD-235HG
* TEAC FD-335HF
* Y-E Data YD-702B-6039B (x2)

Collection:

Floppy drive collection


Topic: Mundane, by Kjetil @ 02/04-2019, Article Link

64-Bit Number Conversion Tool

Around 10 years ago I posted about a number conversion tool. Several improvements have made it into the tool in those years, so it's time for an update. The most notable change is the support for 64-bit resolution on the numbers (also when compiled on 32-bit systems). In addition there is support for more operators like and (&), or (|), multiplication (*), division (/) and modulus (%).

Here is the updated code:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h> /* __WORDSIZE */

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

static char *convert_to_bin(uint64_t integer)
{
  static char buffer[65]; /* Should not be longer than 64-bits + NULL. */
  int i, first_one;

  buffer[64] = '\0'; /* Always terminate. */

  first_one = 63;
  for (i = 63; i >= 0; i--) {
    if (integer >= power_of_two(i)) {
      buffer[63 - i] = '1';
      if (first_one > (63 - i))
      {
        first_one = (63 - i);
      }
      integer -= power_of_two(i);
    } else {
      buffer[63 - i] = '0';
    }
  }

  return &buffer[first_one];
}

static uint64_t convert_from_bin(char *number)
{
  uint64_t value;
  int n;
  char *p;

  value = 0; 
  n = strlen(number) - 3; /* Also minus "0b". */
  p = &number[2];
  do {
    if (*p == '0') {
      n--;
    } else if (*p == '1') {
      value += power_of_two(n);
      n--;
    }
  } while (*p++ != '\0');

  return value;
}

static uint64_t convert_to_int(char *number)
{
  uint64_t integer;

  if (strncmp(number, "0x", 2) == 0) {
    integer = strtoull(number, NULL, 16);
  } else if (strncmp(number, "0b", 2) == 0) { 
    integer = convert_from_bin(number);
  } else if (strncmp(number, "-", 1) == 0) {
    integer = atoll(number); /* Handle signed integers. */
  } else {
    integer = strtoull(number, NULL, 10);
  }
  
  return integer;
}

static uint64_t biggest_int(uint64_t n1, uint64_t n2, uint64_t n3)
{
  /* NOTE: Does not handle signed integers, so padding will be off. */
  if (n1 > n2 && n1 > n3) {
    return n1;
  } else if (n2 > n3) {
    return n2;
  } else {
    return n3;
  }
}

static void display_int(uint64_t integer, char *prefix, uint64_t biggest)
{
  int pad;

  if (prefix != NULL)
  {
    printf("%s", prefix);
  }

#if (__WORDSIZE == 64)
  pad = snprintf(NULL, 0, "%ld", biggest)
      - snprintf(NULL, 0, "%ld", integer);
#else /* __WORDSIZE == 32 */
  pad = snprintf(NULL, 0, "%lld", biggest)
      - snprintf(NULL, 0, "%lld", integer);
#endif /* __WORDSIZE */
  while (pad-- > 0) {
    printf(" ");
  }

#if (__WORDSIZE == 64)
  printf("%ld ", integer);
#else /* __WORDSIZE == 32 */
  printf("%lld ", integer);
#endif /* __WORDSIZE */

  printf("0x");
#if (__WORDSIZE == 64)
  pad = snprintf(NULL, 0, "%lx", biggest)
      - snprintf(NULL, 0, "%lx", integer);
#else /* __WORDSIZE == 32 */
  pad = snprintf(NULL, 0, "%llx", biggest)
      - snprintf(NULL, 0, "%llx", integer);
#endif /* __WORDSIZE */
  while (pad-- > 0) {
    printf("0");
  }

#if (__WORDSIZE == 64)
  printf("%lx ", integer);
#else /* __WORDSIZE == 32 */
  printf("%llx ", integer);
#endif /* __WORDSIZE */

  printf("0b");
  pad = strlen(convert_to_bin(biggest))
      - strlen(convert_to_bin(integer));
  while (pad-- > 0) {
    printf("0");
  }

  printf("%s\n", convert_to_bin(integer));
}

int main(int argc, char *argv[])
{
  uint64_t n1, n2, result, biggest;
  char *prefix;

  if (argc == 2) { /* Just display the number in different forms. */
    n1 = convert_to_int(argv[1]);
    display_int(n1, NULL, n1);

  } else if (argc == 4) { /* Perform extra operation. */
    n1 = convert_to_int(argv[1]);
    n2 = convert_to_int(argv[3]);
    if (argv[2][0] == '+') {
      result = n1 + n2;
      prefix = " + ";
    } else if (argv[2][0] == '-') {
      result = n1 - n2;
      prefix = " - ";
    } else if (argv[2][0] == '^') {
      result = n1 ^ n2;
      prefix = " ^ ";
    } else if (argv[2][0] == '&') {
      result = n1 & n2;
      prefix = " & ";
    } else if (argv[2][0] == '|') {
      result = n1 | n2;
      prefix = " | ";
    } else if (argv[2][0] == '*') {
      result = n1 * n2;
      prefix = " * ";
    } else if (argv[2][0] == '/') {
      result = n1 / n2;
      prefix = " / ";
    } else if (argv[2][0] == '%') {
      result = n1 % n2;
      prefix = " % ";
    } else {
      fprintf(stderr, "%s: error: invalid operator.\n", argv[0]);
      return -1;
    }
    biggest = biggest_int(n1, n2, result);
    display_int(n1, "   ", biggest);
    display_int(n2, prefix, biggest);
    display_int(result, " = ", biggest);

  } else {
    fprintf(stderr, "Usage: %s <number> [<operator> <number>]\n", argv[0]);
    fprintf(stderr, "  number can be integer decimal, hex (0x) or binary (0b)\n");
    fprintf(stderr, "  valid operators: + - ^ & | * / %%\n");
    return -1;
  }
   
  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 09/03-2019, Article Link

Battery Leakage Report

I have several old retro PCs lying around. And as explained in detail here, battery leakage is a risk to some of these PCs depending on the type of battery. So I have gone through a total of 13 PCs and removed leaking NiCd and NiMH batteries from 4 of them, which had those. I have compiled a complete report of all the PCs with image collages here.

Information on the following branded PCs can be found in the report (in addition to 4 custom builds):
* Commodore PC 20-III
* Commodore PC 30-III
* IBM PS/ValuePoint 433DX/Si
* Zenith ICL-4434-KT
* NCR System 3330
* Compaq Deskpro 386s/20
* Compaq ProLinea 590
* Brother BCN3386SX/52
* Laser 486DX2-50E

Here is one of the worst cases of damage:

NiCd battery leak


Topic: Mundane, by Kjetil @ 09/02-2019, Article Link

uIP support for GR-SAKURA

I have now managed to get the uIP TCP/IP network stack running on the GR-SAKURA board. I'm using the Renesas PHY driver, but my own heavily modified (and simplified) implementation of the Ethernet driver.

The Renesas Ethernet controller has a "Zero Copy" mechanism that uses DMA transfer on incoming and outgoing packets. This is nice and all, but it comes into conflict with the uIP architecture with a single buffer. So I ended up with the separate single uIP buffer and two separate ethernet buffers instead, doing CPU based (memcpy) copying between them. Even though this is not as efficient, it prevents the need for any modifications to the core uIP source code.

One limitation with this initial implementation is that there is no handling of link up or down. The code expects the Ethernet plug to be plugged in at boot and remain connected.

To start using it, get the code from here and unpack it in the uIP source folder. (The "sakura" directory should be alongside the "unix" directory.) Then run "make" inside the "sakura" directory to create the "uip.bin" binary that may be uploaded the GR-SAKURA board.

I have also forked the original uIP repository and made the necessary changes on GitHub.

Topic: Open Source, by Kjetil @ 01/01-2019, Article Link

Joystick Mouse on GR-SAKURA

Here is another project based on the GR-SAKURA board. It translates joystick movement into mouse movement, so it can be used on a PC. This is done reading three analog inputs from the joystick and forwarding data over an UART using the simple Microsoft Serial Mouse format.

The software is based heavily on the 30JH joystick that I happen to have. This is a 3-axis joystick, and the last axis is used to simulate the button presses.

This command in Linux can be used to get it connected:

sudo inputattach -bare /dev/ttyUSB0
          


Here's a photo of the setup:

GR-SAKURA wth Joystick


Here is the main part of the code, which implements the serial mouse protocol:

#include "uart.h"
#include "adc.h"

/* Raw ADC reference values: */
#define JOYSTICK_POS_MAX 2464
#define JOYSTICK_POS_MIN 2080 /* JOYSTICK_POS_MAX - (128 * 3) */
#define JOYSTICK_CENTER  2048
#define JOYSTICK_NEG_MIN 2016 /* JOYSTICK_NEG_MAX + (128 * 3) */
#define JOYSTICK_NEG_MAX 1632

static unsigned char prev_buttons = 0;

static unsigned char convert_adc_to_move_value(short value)
{
  /* Scaling and manual conversion to two's complement. */
  if (value > JOYSTICK_POS_MIN) {
    if (value > JOYSTICK_POS_MAX) {
      value = JOYSTICK_POS_MAX;
    }
    return (value - (JOYSTICK_POS_MIN + 1)) / 3;

  } else if (value < JOYSTICK_NEG_MIN) {
    if (value < JOYSTICK_NEG_MAX) {
      value = JOYSTICK_NEG_MAX;
    }
    return ((value - (JOYSTICK_NEG_MAX + 1)) / 3) + 128;

  } else {
    return 0; /* Filtered. */
  }
}

static unsigned char convert_adc_to_button_value(short value)
{
  if (value > JOYSTICK_POS_MIN) {
    return 0b10; /* Left button pressed. */

  } else if (value < JOYSTICK_NEG_MIN) {
    return 0b01; /* Right button pressed. */

  } else {
    return 0b00; /* No buttons pressed. */
  }
}

static short invert_adc_value(short value)
{
  if (value > JOYSTICK_CENTER) {
    return JOYSTICK_CENTER - (value - JOYSTICK_CENTER);
  } else {
    return JOYSTICK_CENTER + (JOYSTICK_CENTER - value);
  } 
}

static int mouse_format(short x_move_adc, short y_move_adc,
                        short button_adc, unsigned char *data)
{
  unsigned char x_move, y_move, buttons;

  x_move = convert_adc_to_move_value(x_move_adc);
  y_move = convert_adc_to_move_value(invert_adc_value(y_move_adc));
  buttons = convert_adc_to_button_value(button_adc);

  data[0] = 0b11000000;
  data[1] = 0b10000000;
  data[2] = 0b10000000;

  data[0] |= (buttons << 4);
  data[0] |= ((y_move >> 4) & 0b00001100);
  data[0] |= ((x_move >> 6) & 0b00000011);
  data[1] |= (x_move & 0b00111111);
  data[2] |= (y_move & 0b00111111);

  if (y_move > 0 || x_move > 0 || buttons != prev_buttons) {
    prev_buttons = buttons;
    return 1; /* Activity */
  } else {
    return 0; /* No activity */
  }
}

void mouse_loop(void)
{
  unsigned char data[3];

  while(1) {
    asm("wait");
    if (mouse_format(adc_get(0), adc_get(1), adc_get(2), data)) {
      uart0_send(data, 3);
    }
  }
}
          


Get the complete code under the MIT License here, which requires the RX GCC toolchain to compile.

Topic: Open Source, by Kjetil @ 12/12-2018, Article Link

eLua support for GR-SAKURA

I have managed to get eLua running on the GR-SAKURA board. It's an embedded version of version of The Programming Language Lua that can be run on bare-metal.

I forked the original eLua repository and made the necessary changes here on my own copy. Note however that this initial support is very basic; only limited versions of the "tmr", "uart" and "pio" modules are supported at the moment.

Here is an example Lua script that can be run on the board. Name it "autorun.lua" and put it in the "romfs/" directory to run it automatically on startup:

require "pio"
require "pd"
require "tmr"

print("Running LEDs on " .. pd.board())
print("Hold blue button to stop.")

while pio.pin.getval(7) == 1 do
  pio.pin.sethigh(0)
  tmr.delay(0,200000)
  pio.pin.sethigh(1)
  tmr.delay(0,200000)
  pio.pin.sethigh(2)
  tmr.delay(0,200000)
  pio.pin.sethigh(6)
  tmr.delay(0,200000)

  pio.pin.setlow(0)
  tmr.delay(0,200000)
  pio.pin.setlow(1)
  tmr.delay(0,200000)
  pio.pin.setlow(2)
  tmr.delay(0,200000)
  pio.pin.setlow(6)
  tmr.delay(0,200000)
end

print("Finished.")
          


Topic: Open Source, by Kjetil @ 09/11-2018, Article Link

GR-SAKURA Shell Update

I have added two new commands to the GR-SAKURA Shell. One is just a "button" command to print the state of the blue push button, known as SW2. The other "adc" command prints the binary value of the 12-bit A/D converter connected to analog inputs AN0 to AN5.

ADC command:

sakura> adc
AN0: 101111011010
AN1: 110000101110
AN2: 110001111111
AN3: 100000001000
AN4: 000000000001
AN5: 110000110001
sakura> adc
AN0: 101110111011
AN1: 110000000010
AN2: 110001010111
AN3: 110001000001
AN4: 011111000010
AN5: 000000000011
          


Button command:

sakura> button
released
sakura> button
pressed
          


Get the source code here or from my GitLab or GitHub pages.

Also consider using my updated instructions on to build the cross compilation toolchain for the Renesas RX CPU.

Topic: Open Source, by Kjetil @ 11/10-2018, Article Link

Reverse SSH Tunnel Launcher

Here is a script which helps with starting a reverse SSH tunnel. It gets the instructions from a web server, in the form of a JSON file, which determines at what time to open the tunnel(s). The JSON file also determines when to check for a JSON file next time. It operates on a daily schedule and uses the HH:MM format.

The idea is to leave this script running on a box behind a firewall. It is hardcoded to only keep the tunnel open for 5 minutes before closing it again. So to get a connection that lasts longer, a new tunnel probably has to be made manually after connecting.

The Python code:

#!/usr/bin/python

import urllib2
import json
import subprocess
import signal
import datetime
import time
import logging
import sys

config_url = "http://192.168.0.1/config.json"
ssh_host = "192.168.0.1"
ssh_port = 22
local_port = 1337
tunnel_open_time = 300 # In seconds.

tunnel = None

def alarm_handler(signum, frame):
    global tunnel
    logger.info("Terminating current tunnel.")
    tunnel.kill();
    tunnel = None

def minutes_until(minute_ts):
    if minute_ts == None:
        return (24 * 60)

    now = datetime.datetime.now().timetuple()
    now_ts = (now.tm_hour * 60) + now.tm_min

    if now_ts > minute_ts:
        # Already passed, will be next day.
        return minute_ts - now_ts + (24 * 60)
    else:
        return minute_ts - now_ts

def update_config():
    try:
        config = json.load(urllib2.urlopen(config_url))

        # Pick closest time to update next time:
        closest_config_ts = None
        for uc in config["ConfigUpdate"]:
            config_ts = (int(uc.split(":")[0]) * 60) + int(uc.split(":")[1])
            if minutes_until(config_ts) < minutes_until(closest_config_ts):
                closest_config_ts = config_ts

        tunnel_ts = list()
        for to in config["TunnelOpen"]:
            tunnel_ts.append((int(to.split(":")[0]) * 60) + int(to.split(":")[1]))

        logger.debug("Config timestamp: %d" % (closest_config_ts))
        logger.debug("Tunnel timestamps: %s" % (str(tunnel_ts)))

        return closest_config_ts, tunnel_ts

    except Exception, e:
        logger.error("Exception during config update: %s" % (e))
        return 0, []

if __name__ == "__main__":
    logger = logging.getLogger('reverse_tunnel')
    logger.setLevel(logging.DEBUG)
    logger.addHandler(logging.StreamHandler())

    # Get initial configuration:
    config_ts, tunnel_ts = update_config()
    if len(tunnel_ts) == 0:
        sys.exit(1) # Exit right away if initial configuration fails.

    signal.signal(signal.SIGALRM, alarm_handler)

    while True:
        now = datetime.datetime.now().timetuple()
        now_ts = (now.tm_hour * 60) + now.tm_min

        # Is it time for a config update?
        if now_ts == config_ts:
            logger.info("Timed config update.")
            config_ts, tunnel_ts = update_config()
            if config_ts == now_ts:
                config_ts = config_ts - 2 # Prevent immediate update.

        # Is it time to open a new tunnel?
        for ts in tunnel_ts:
            if now_ts == ts:
                if tunnel == None:
                    logger.info("Opening new tunnel.")
                    tunnel = subprocess.Popen(["ssh", "-R", str(local_port) + ":localhost:22", "-N", "-p", str(ssh_port), ssh_host])
                    signal.alarm(tunnel_open_time)
                else:
                    logger.warning("Tunnel already running.")

        time.sleep(10)
          


Example JSON file:

{
  "ConfigUpdate" : ["17:00"],
  "TunnelOpen" : ["18:00","19:00","20:00"]
}
          


Topic: Scripts and Code, by Kjetil @ 29/09-2018, Article Link

Older articles

Newer articles