編集履歴一覧に戻る
eucalyのアイコン画像

eucaly が 2022年10月29日17時30分35秒 に編集

初版

タイトルの変更

+

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

タグの変更

+

Arduino

+

モーター

+

エミュレーター

メイン画像の変更

メイン画像が設定されました

記事種類の変更

+

製作品

ライセンスの変更

+

(MIT) The MIT License

本文の変更

+

こんにちは、ゆうかりです。 作業場の撤収やら引っ越しやら新規構築やらでバタバタしており、なかなか電子工作回りをやる余裕が無いんですけれども、まあ。 禁断症状?的な感じなので、前から気になってたネタを、やらかしてみようと思います。 それは・・・。 表題の通り、「コンピューター用テープドライブの不思議な動きを確かめてみる」です。 # そもそもテープドライブって何よ? ひと昔前はありふれていた、「テープドライブ」。 まあほら、音楽用のカセットテープとか、VHSビデオテープとかその辺です。 コンピューター用では、「LTO」とか「AIT」、「DDS」だの「QIC」だのありますね。 まあ、一部例外を除き、こんな構造をしています。 ![キャプションを入力できます](https://camo.elchika.com/df3d23cc08b1434012716db78cc809109f9577c9/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666637633237372d646465642d343333632d393736342d6162303436346433326631302f31326163383037612d306465612d346638612d393538632d623038366166376564633133/)  *出典:Wikipedia ま、テープは左右の白「ハブ」に巻き取られる形で。 まあ、テープ巻き取り直径的な速度の違いはあるにせよ、左右のハブは同期して回ります。 じゃないとテープ切れちゃいます、からね。 今時、というか、ほぼすべてのテープドライブは、この「左右の巻き取りハブ」は、バラバラに動くことはありません。 が、しかし # 9-track tape driveの不思議 だいぶ昔の、コンピュータ用テープドライブは、不思議な動きをします。 まあ、下の動画をどうぞ。 @[youtube](https://www.youtube.com/watch?v=SGgsiBHsves) なんか、右と左のテープハブ、ばらばらに動いてるように見えませんか?。 実際、ある程度ばらばらに動いています。 オイラがご幼少の頃は、これが不思議だったのですが。 まあ、ある程度裏事情的なものが察せられる現在、なーんとなく理由が見えてきています。 じゃあなによ?って。 # 9-track tape driveの規格と構造 ま、これは、「9-track tape」という、けっこーかなり古い規格です。 なんせIC使われる前、レベルの。 ま、こんなのです。 ![キャプションを入力できます](https://camo.elchika.com/522e68e55675a145ba787fb53895737f8458ae7b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666637633237372d646465642d343333632d393736342d6162303436346433326631302f36373761646434302d313262612d343464302d386430302d666665386337376535306564/)  *出典: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なテープを止めたり逆回転とかをそのままやらかしたら、テープは一瞬で切れちゃいます。 なので、そんなトリックプレイに耐えられるように、テープ読み込み部に、「遊び」が設けられています。 この写真の ![キャプションを入力できます](https://camo.elchika.com/4e5184885bd79b5d42d22d01bc7b3cb62ad102a1/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666637633237372d646465642d343333632d393736342d6162303436346433326631302f39346132616132612d666235662d346566642d613838622d626666383266336334373834/) テープドライブ下にある、||な部分が、テープ遊び用の空間です、「真空チャンバー」なるところ。 実際のテープ読み取り部は、テープハブ下側中央黒い長四角部なのですが、そこにテープが行く前に、テープは下の||部に垂れ下がります。 ここは真空引きされており、テープを「優しく」吸引することで、急速なテープの走行に対して、テープにテンションがかかりづらいようにしているのです。 んで、これが、上の動画にあった、左右のテープハブがバラバラに動いて見える原因を作っています。 左右の真空チャンバーに送り込まれたテープは、それぞれ一定までテープを送り、吸い込むため、一定時間は静止している状態です。 真空チャンバーに溜まったテープが多すぎたり、余裕が少なくなったりして初めて、左右のテープハブが動く構造になっているため、実際の読み込みと左右のテープハブが同期せず、バラバラに動くように見えるのです。 ちなみに内部構造はこんな感じ、ごついモーターと大量のクラッチ、ブレーキ、更に真空引き用ポンプとかがどっさりと。 @[youtube](https://www.youtube.com/watch?v=8qZEQIX7VY8) # で、この非同期なハブ構造と楽しんでみようかと 秋月電子とかで、適当に部材購入!。 ![キャプションを入力できます](https://camo.elchika.com/68ddf73aed0f81347a1b3cc34d5284fdcb55bbaa/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666637633237372d646465642d343333632d393736342d6162303436346433326631302f65623765616165322d366237302d346465352d613563612d613738323862343334343332/) 制御とモーター系は、この組み合わせで。 ![キャプションを入力できます](https://camo.elchika.com/51aa941b3c612014c1a2ca09084d09a2d437e360/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666637633237372d646465642d343333632d393736342d6162303436346433326631302f33323630303835662d303862322d346131302d626537342d393431396632343262623663/) arduino互換の「maker nano」と、I2Cなモータードライバ「DRV8830」、そして適当なDCモーターです。 まあ、アドレス違いでブレッドボード上に組むと、こんな感じになります。 ![キャプションを入力できます](https://camo.elchika.com/5b01e5365f4f573817c0b7c861c5284695e330e5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666637633237372d646465642d343333632d393736342d6162303436346433326631302f64366264653239302d643062312d343135312d393236302d313934366665303936323065/) 適当に習作プログラムを組めば、まあ普通にモーター動作しましたよ、と。 SENSE端子をGNDに落とさないと、エラー吐きますが。 # 走行用データ用意 さて、ランダムアクセス用のテープ走行データをでっちあげないといけませぬ。 左右の走行を切り替えて、視覚的に楽しいのといえば、ファミコンのTASかなと。 つーわけで、以下ページから、fm2ファイルを取得。 https://tasvideos.org/1546M fm2ファイルは、fceuxというファミコンエミュレータのムービーファイルです。 ざっと中身を確認するに、フレーム毎に「押すボタン」を記述する形式のようです。 fceuxのフレームレートは約60.001fpsらしいので、ま、大体18msごとに読み込むようにしてやればいいかなと。 で、このままだとArduinoに食わせられないので、「左右」のボタンOn/Offを2ビットで表現した上で、4つ束ねてバイト列にするスクリプトを記述。 ```python: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領域に展開し、順次メモリに読み込みしつつモーター制御するプログラムを書いてみました。 ```c: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との対比は、こんな感じで!。 @[youtube](https://youtu.be/d8mHj83pA-w) なんとなく、左右の回転差や、非同期具合が再現できているような気もします。 # 作ってみて なかなか不思議な、テープの動きが、ある程度再現出来て、楽しめましたとさ。 ちゃんと「テープドライブ」らしい、タンスみたいな箱に作りこみたいけれども、工作機械の稼働が出来ないため、今回はブレッドボードで満足としました、とさ。 いやあ、モーター系は初めて手を出してみたんだけれども、なかなか興味深い世界でした。 次はステッピングモーターでも遊んでみようかな・・・。 以上です。