送信(transmitting)とは、シリアルポートを通じてコンピュータからバイ ト列を送ることです。ここで最初に示す例は極端に簡略化したものです。 もっと詳しい説明を後で追加します。 コンピュータがシリアルポートから 1 バイトのデータを送ろうとする時、CPU はコンピュータ内部のバス上にあるそのデータをシリアルポートの I/O アド レスに送ります。シリアルポートはそのデータを受け取り、シリアル端子の 送信ピンを使ってこれを 1 ビットずつ送ります(シリアルビットストリーム)。 あるビット(とバイト)が電気的にはどう見えるのかについては、 電圧の波形の章をご覧ください。この章に書いてあ ることは全てバイトデータの送信に関するものです(シリアルポート内にある 別のハードウェアを用いる受信については触れていません)。
上記の説明をもう少し詳しくして(しかしまだ完全には程遠いものです)繰り返 します。シリアルポートで行われる処理のほとんどは UART (または同等のも の)が行います。1 バイトのデータを転送するために、シリアルデバイスドラ イバプログラム(CPU 上で動作)はシリアルポートの I/O アドレスに 1 バイト を送ります。このデータはシリアルポートの「送信シフトレジスタ」(大きさ が 1 バイト)に入ります。このシフトレジスタでは、このバイトデータから 1 ビットずつ取り出され、1 ビットずつシリアル線に送られます。そして最後の ビットが送信されると、シフトレジスタは送るための別のバイトが必要になる ので、CPU に別のバイトデータを送るように求めます。これなら単純な話です が、CPU はバイトデータを即座に取得することができないかもしれないので、 遅延を考慮しなければならないでしょう。結局、CPU は普通は単なるシリアル ポートの処理以外のことを行っています。
シフトレジスタにバイトデータを取得する際の遅延をなくす方法は、シフトレ ジスタがバイトデータを必要とする前に CPU がバイトデータを取得して、こ れをシリアルポートの(ハードウェア)バッファに格納することです。それから、 シフトレジスタがバイトデータを送り出して新しいバイトが即座に必要になる と、シリアルポートのハードウェアは自分のバッファの次のバイトをシフトレ ジスタに転送するだけです。これを行うために CPU を呼ぶ必要はありません。
シリアルポートのバッファのサイズは元々 1 バイトしかありませんでしたが、 現在は普通 16 バイトです(高価なシリアルポートにはもっとあります)。しか しそれでも、シフトレジスタが転送するデータを必要とした時に必ずバッファ にデータがあるように、このバッファに十分なバイトデータを与え続けるとい う問題が残っています(送るデータがもう残っていない場合は除きます)。これ は割り込みを使って CPU と連絡することで行います。
古い方式のバッファが 1 バイトのシリアルポートはこのように動作します。 シフトレジスタがバイトデータをバッファから取り出し、バッファが他のバイ トデータを必要とすると、コンピュータのバス上にある専用の配線の電圧を上 げることにより、CPU に割り込みが送られます。CPU が非常に重要な処理をし ていなければ、CPU が実行中の処理は割り込みにより強制的に中断され、シリ アルポートのバッファに別のバイトデータを与えるためのプログラムが実行さ れます。このように、このバッファの目的は、シリアルポートから送り出され るバイトが途切れないように、いつでも出せるようなバイトデータを(送られ るのを待って)ハードウェア内のキューに入れた状態にしておくことです。
一度 CPU に割り込みがかかると、CPU は誰が割り込みをかけたか知ることが できます。なぜなら、各シリアルポートについて専用の割り込み線があるから です(ただし割り込みの共有をしていない場合)。それから CPU はシリアルデ バイスドライバを動作させます。ドライバは I/O アドレスにあるレジスタを チェックし、何が起きたのかを調べます。そして、シリアルポートの送信バッ ファが空になり、追加のバイトデータを待っていることを知ります。したがっ て、送るべきバイトデータがもっとあれば、CPU は次のバイトデータをシリア ルポートの I/O アドレスに送ります。このバイトは、前のバイトがまだ送信 シフトレジスタ内にあり、1 ビットずつ送られている間に届かなくてはなりま せん。
復習すると、1 つのバイトがシリアルポートの送信線へ完全に送り出されると、 シフトレジスタは空になり、以下の 3 つの動作がほとんど同時に起こります:
シリアルポートは割り込み駆動(interrupt driven)であると言えます。 シリアルポートが割り込みを発行する度に、CPU は次のバイトを送ります。 CPU が 1 バイトを送信バッファに送ると、次の割り込みを受け取るまで、CPU は他の処理を自由に行うことができます。シリアルポートはユーザ(あるいは アプリケーションプログラム)が選択した 固定の速度でビット列を送信します。この速度はボーレートと呼ばれることも あります。シリアルポートはバイトごとに追加のビット(スタートビット、ス トップビット、場合によってはパリティビットも)も付け加えるので、多 くの場合、1 バイトごとに 10 ビットのデータが送られます。したがって、通 信レート(速度とも言われます) 19,200 ビット/秒(bps, bit per second)は 1,920 バイト/秒(1,920 割り込み/秒)ということになります。
この処理を全て行うことは CPU にとっても重い負担です。これは色々な理由 から言えます。まずは、32 ビット(64 ビットのことだってあります)のバス上 で一回に 1 バイト(8 ビット)のデータしか送らないので、バス幅をあまり有 効に使っているとは言えません。また、割り込みを送る度に大きなオーバーヘッ ドが生じます。割り込みを受け取った場合でも、デバイスドライバに分かるこ とは何かがシリアルポートで割り込みを起こしたことだけであり、文字が送ら れたことが理由であることまでは分かりません。何が起こったかを調べるには、 デバイスドライバは色々なチェックを行わなければなりません。同じ割り込み が起こっても、文字を受け取ったのかもしれませんし、制御線の状態が変化し たのかもしれません。
解決方法の一つは、シリアルポートのバッファの大きさを増やすことでした。 現在のシリアルポートの大部分は、1 バイトだけではなく、16 バイトのバッ ファを持っています。つまり、CPU は割り込みを受け取ると、シリアルポート に 16 バイトまでのデータを新たに送ることができます。これにより割り込み の発行は減りますが、太いバスにもかかわらず 1 バイトずつしかデータを送 れない問題はそのままです。16 バイトのバッファは実際には FIFO (First In First Out, 先入れ先出し)のキューです。上記の一部を繰り返して もう少し詳しく説明したものについては、 FIFO を ご覧ください。
シリアルポートによるバイト列の受信は送信とほぼ同じで、方向が逆になっ ているだけです。受信も割り込み駆動です。受信バッファを 1 バイトしか持 たない古い型のシリアルポートでは、1 バイトのデータ全体を受け取ると、こ のデータは(大きさが 1 バイトである)受信バッファに入ります。するとシリ アルポートは CPU に割り込みをかけてそのデータを取り込ませ、現在受信中 のデータを格納できる場所が空くようにします。16 バイトのバッファを持っ ている新しいシリアルポートの場合は、この(バイトデータを取得するための) 割り込みはバッファに 14 バイトのデータが蓄積した時点で発行されます。す ると CPU は現在の処理を一旦止め、1 から 16 バイトのデータをシリアルポー トから取り出します。14 番目のバイトが受け取られた時に送られた割り込み に対し、これが起きてからさらに 2 つのバイトデータが届くと、受け取るべ きバイトの個数は 16 バイトになるかもしれません。しかし(2 バイトではな く) 3 バイト届いてしまうと、16 バイトのバッファは溢れてしまいます。
今まではシリアルポートが持っている小さい(1 または 16 バイト)ハード ウェアバッファについて説明しましたが、メインメモリにはもっと大きな バッファもあります。シリアルポートがバイトデータ(1 から 16 バイト)を ハードウェアの受信バッファから取り込んだ時、CPU はこれらデータをメイン メモリ中の大きな(例えば 8K バイトの)バッファに入れます。したがって、 シリアルポートからデータを受け取るプログラムは、この大きなバッファから (プログラム中で "read" 文を用いて)データを取り出します。送信するデータ についても同じことが言えます。CPU がデータをいくらか送る必要がある場合、 CPU はメインメモリにある大きい(8K バイトの)送信バッファからデータを取 り出し、これらをハードウェアが持つ小さな(1 または 16 バイトの)送信バッファ に入れます。