eucalyのアイコン画像
eucaly 2022年10月29日作成 (2022年10月29日更新) © MIT
製作品 製作品 閲覧数 1470
eucaly 2022年10月29日作成 (2022年10月29日更新) © MIT 製作品 製作品 閲覧数 1470

コンピューター用テープドライブの不思議な動きを確かめてみる

コンピューター用テープドライブの不思議な動きを確かめてみる

こんにちは、ゆうかりです。

作業場の撤収やら引っ越しやら新規構築やらでバタバタしており、なかなか電子工作回りをやる余裕が無いんですけれども、まあ。
禁断症状?的な感じなので、前から気になってたネタを、やらかしてみようと思います。

それは・・・。
表題の通り、「コンピューター用テープドライブの不思議な動きを確かめてみる」です。

そもそもテープドライブって何よ?

ひと昔前はありふれていた、「テープドライブ」。
まあほら、音楽用のカセットテープとか、VHSビデオテープとかその辺です。
コンピューター用では、「LTO」とか「AIT」、「DDS」だの「QIC」だのありますね。

まあ、一部例外を除き、こんな構造をしています。
キャプションを入力できます
 *出典:Wikipedia

ま、テープは左右の白「ハブ」に巻き取られる形で。
まあ、テープ巻き取り直径的な速度の違いはあるにせよ、左右のハブは同期して回ります。
じゃないとテープ切れちゃいます、からね。

今時、というか、ほぼすべてのテープドライブは、この「左右の巻き取りハブ」は、バラバラに動くことはありません。
が、しかし

9-track tape driveの不思議

だいぶ昔の、コンピュータ用テープドライブは、不思議な動きをします。
まあ、下の動画をどうぞ。

ここに動画が表示されます

なんか、右と左のテープハブ、ばらばらに動いてるように見えませんか?。
実際、ある程度ばらばらに動いています。

オイラがご幼少の頃は、これが不思議だったのですが。
まあ、ある程度裏事情的なものが察せられる現在、なーんとなく理由が見えてきています。
じゃあなによ?って。

9-track tape driveの規格と構造

ま、これは、「9-track tape」という、けっこーかなり古い規格です。
なんせIC使われる前、レベルの。

ま、こんなのです。
キャプションを入力できます
 *出典:Wikipedia
 *詳しい解説は、Wikipediaへ!→ https://en.wikipedia.org/wiki/9-track_tape

なんであんな不思議な動きになるかというと、まあこれは今でいう「HDD」みたいな使い方をされていて。
ランダムアクセスをさせていたから、なのと。
ランダムアクセスに耐えられるように工夫が入っていたから、です。

IBMのSystem/360に使われていた、2401というテープドライブとテープの規格は、以下な感じです。

項目 Model 1 Model 2 Model 3
データレート (8bit bytes/sec) 30KB/60KD 60KB/120KD 90KB/180KD
テープ速度 (インチ/sec) 37.5 75.0 112.5
巻き戻し時間(分) 3.0 1.4 1.0
項目 内容
テープ長さ 最大2400フィート
記録密度 800Bytes / インチ

* 出典: https://www.ibm.com/ibm/history/exhibits/storage/storage_2401.html

モデル3だとして、テープ速度は112.5インチ/秒、約2.9m/秒です、時速約10km。
一般的なテープだと、例えばカセットテープは秒速4.8cm、VHSは標準で秒速33cm、音楽用オープンテープの民生用で一番早いのが秒速38cmです。
まあ、ざっとそのへんのテープの10倍くらいの速度で、コンピュータ用テープは、ぶんまわっていたのです。
しかもランダムアクセス!。
なお、1巻最大2400フィートで、記録密度はインチ当たり800バイトなので、1巻で約24MBですね、、、。

で、まあ、時速10kmなテープを止めたり逆回転とかをそのままやらかしたら、テープは一瞬で切れちゃいます。
なので、そんなトリックプレイに耐えられるように、テープ読み込み部に、「遊び」が設けられています。

この写真の
キャプションを入力できます

テープドライブ下にある、||な部分が、テープ遊び用の空間です、「真空チャンバー」なるところ。
実際のテープ読み取り部は、テープハブ下側中央黒い長四角部なのですが、そこにテープが行く前に、テープは下の||部に垂れ下がります。
ここは真空引きされており、テープを「優しく」吸引することで、急速なテープの走行に対して、テープにテンションがかかりづらいようにしているのです。

んで、これが、上の動画にあった、左右のテープハブがバラバラに動いて見える原因を作っています。
左右の真空チャンバーに送り込まれたテープは、それぞれ一定までテープを送り、吸い込むため、一定時間は静止している状態です。
真空チャンバーに溜まったテープが多すぎたり、余裕が少なくなったりして初めて、左右のテープハブが動く構造になっているため、実際の読み込みと左右のテープハブが同期せず、バラバラに動くように見えるのです。

ちなみに実際の動作は、こんな感じ、テープロード/アンロード、真空チャンバーとテープの関係が分かるかと。

ここに動画が表示されます

で、この非同期なハブ構造と楽しんでみようかと

秋月電子とかで、適当に部材購入!。
キャプションを入力できます

制御とモーター系は、この組み合わせで。
キャプションを入力できます
arduino互換の「maker nano」と、I2Cなモータードライバ「DRV8830」、そして適当なDCモーターです。

まあ、アドレス違いでブレッドボード上に組むと、こんな感じになります。
キャプションを入力できます

適当に習作プログラムを組めば、まあ普通にモーター動作しましたよ、と。
SENSE端子をGNDに落とさないと、エラー吐きますが。

走行用データ用意

さて、ランダムアクセス用のテープ走行データをでっちあげないといけませぬ。

左右の走行を切り替えて、視覚的に楽しいのといえば、ファミコンのTASかなと。
つーわけで、以下ページから、fm2ファイルを取得。
https://tasvideos.org/1546M

fm2ファイルは、fceuxというファミコンエミュレータのムービーファイルです。
ざっと中身を確認するに、フレーム毎に「押すボタン」を記述する形式のようです。
fceuxのフレームレートは約60.001fpsらしいので、ま、大体18msごとに読み込むようにしてやればいいかなと。
で、このままだとArduinoに食わせられないので、「左右」のボタンOn/Offを2ビットで表現した上で、4つ束ねてバイト列にするスクリプトを記述。

gplay.py

f = open('aglar,hotarubi-gimmickbestending.fm2', 'r') count = 15 bitarray = '' while True: data = f.readline() if data == '': break if data[0] != '|': continue if data[3] == 'R': bitarray += '01' elif data[4] == 'L': bitarray += '10' else: bitarray += '00' if len(bitarray) == 8: intdata = int(bitarray, 2) hexdata = str(hex(intdata)[2:].zfill(2)) + ',' print ('0x' + hexdata, end='') bitarray = '' count += 1 if count % 16 == 15: print ('') print (' ', end='') f.close()

上の出力をAVRのFlash領域に展開し、順次メモリに読み込みしつつモーター制御するプログラムを書いてみました。

tapesoukou.ino

#include <avr/pgmspace.h> #include <Wire.h> const int driveraddress1 = 0x64; const int driveraddress2 = 0x63; unsigned long timepre; unsigned long timepost; unsigned long timedelta; int vcol1; int vcol2; int motorrun1 = 0; int motorrun2 = 0; const int motorspeed1 = 5; const int motorspeed2 = 6; byte bitleft; byte bitright; byte bitpoint; byte movebasedata; int movearraycount; byte movedata; byte movecount; byte tapespeed; byte tapespeedcount; byte tapespeedlow = 2; const int vcollength = 12; const int vcolswitch = 3; const byte vcolhubsize1 = 8; const byte vcolhubsize2 = 3; const PROGMEM byte movearray[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,        -- 中略 -- 0xaa,0x95,0x00,0x05,0x55,0x55,0x55,0x56,0xaa,0xa6,0xaa,0xaa,0xaa,0xaa,0x55,0x54, 0x01,0x55,0x55,0x55,0xaa,0xaa,0x6a,0xaa,0xaa,0xaa,0xa5,0x55,0x55,0x55,0x55,0x55, 0x55,0x55, }; const int movearraylength = sizeof(movearray)/sizeof(movearray[0]); void setup() { Wire.begin(); vcol1 = 0; vcol2 = 0; pinMode(4, OUTPUT); pinMode(5, OUTPUT); runMotor(0,1); runMotor(0,2); delay(5000); } void loop() { timepre = micros(); if (movecount == 0) { movebasedata = pgm_read_byte(movearray + movearraycount); movearraycount += 1; if (movearraycount >= movearraylength) { movearraycount -= 1; delay (10000); movebasedata = 0x00; } } bitpoint = 7 - movecount; bitleft = bitRead(movebasedata, bitpoint); bitpoint -= 1; bitright = bitRead(movebasedata, bitpoint); movecount += 2; if (movecount > 7) { movecount = 0; } digitalWrite(4, LOW); digitalWrite(5, LOW); tapespeed = tapespeedlow; if (tapespeedcount < 20){ tapespeed = tapespeedlow * 5; } if (bitleft == 1) { tapespeedcount += 1; vcol1 = vcol1 + tapespeed; vcol2 = vcol2 + tapespeed; digitalWrite(5, HIGH); } else if (bitright == 1) { tapespeedcount += 1; vcol1 = vcol1 - tapespeed; vcol2 = vcol2 - tapespeed; digitalWrite(4, HIGH); } else { tapespeedcount = 0; } if (vcol1 > vcollength) { motorrun1 = 1; } else if (vcol1 < -vcollength) { motorrun1 = -1; } if ((vcol1 < vcolswitch) && (vcol1 > -vcolswitch)) { motorrun1 = 0; } if (motorrun1 == 1){ runMotor(motorspeed1,1); vcol1 -= vcolhubsize1; } if (motorrun1 == -1){ runMotor(-motorspeed1,1); vcol1 += vcolhubsize1; } if (motorrun1 == 0){ brakeMotor(1); } if (vcol2 > vcollength) { motorrun2 = 1; } else if (vcol2 < -vcollength) { motorrun2 = -1; } if ((vcol2 < vcolswitch) && (vcol2 > -vcolswitch)) { motorrun2 = 0; } if (motorrun2 == 1){ runMotor(motorspeed2,2); vcol2 -= vcolhubsize2; } if (motorrun2 == -1){ runMotor(-motorspeed2,2); vcol2 += vcolhubsize2; } if (motorrun1 == 0){ brakeMotor(2); } timepost = micros(); timedelta = timepost - timepre; if (timedelta > 16660) { timedelta = 16660; } timedelta = 16666 - timedelta; delayMicroseconds(timedelta); } void runMotor(int inVector,byte devicenumber) { int direction; int voltage; if (inVector > 0) { direction = 0x01; voltage = inVector; } else if (inVector == 0) { direction = 0x00; voltage = 0; } else { direction = 0x02; voltage = -inVector; } writeToDriver(direction, voltage, devicenumber); } void brakeMotor(byte devicenumber) { writeToDriver(0x03, 0x00,devicenumber); } void writeToDriver(byte inDirection, byte inVoltage,byte devicenumber) { int outData = (inVoltage & 0x3f) << 2 | (inDirection & 0x03); if (devicenumber == 2) { Wire.beginTransmission(driveraddress2); } else { Wire.beginTransmission(driveraddress1); } Wire.write(0x00); // control Wire.write(outData); // Wire.endTransmission(true); }

左右、一定時間まではサーチということでテープ速度は速めに、左右のハブはテープの偏りがあるって感じで送り出し量に差をつけています。
LEDは実際のテープ走行左右 = TASの左右ボタン入力を表しています。

実際に動かしてみた

ま、TASとの対比は、こんな感じで!。

ここに動画が表示されます

なんとなく、左右の回転差や、非同期具合が再現できているような気もします。

作ってみて

なかなか不思議な、テープの動きが、ある程度再現出来て、楽しめましたとさ。
ちゃんと「テープドライブ」らしい、タンスみたいな箱に作りこみたいけれども、工作機械の稼働が出来ないため、今回はブレッドボードで満足としました、とさ。

いやあ、モーター系は初めて手を出してみたんだけれども、なかなか興味深い世界でした。
次はステッピングモーターでも遊んでみようかな・・・。

以上です。

1
eucalyのアイコン画像
いつも、てきとうです
ログインしてコメントを投稿する