2015年11月21日

メモリ割り当て

  前回の続きです。


  参考文献:Mr.PCテクニカルコレクション 秀和システムトレーディング


○ポート0x60:CPUアドレス:0x0000〜0x1FFFの読み出し設定
○ポート0x61:CPUアドレス:0x2000〜0x3FFFの読み出し設定
○ポート0x62:CPUアドレス:0x4000〜0x5FFFの読み出し設定
○ポート0x63:CPUアドレス:0x6000〜0x7FFFの読み出し設定
○ポート0x64:CPUアドレス:0x8000〜0x9FFFの読み出し設定
○ポート0x65:CPUアドレス:0xA000〜0xBFFFの読み出し設定
○ポート0x66:CPUアドレス:0xC000〜0xDFFFの読み出し設定
○ポート0x67:CPUアドレス:0xE000〜0xFFFFの読み出し設定

○ポート0x68:CPUアドレス:0x0000〜0x1FFFの書き込み設定
○ポート0x69:CPUアドレス:0x2000〜0x3FFFの書き込み設定
○ポート0x6A:CPUアドレス:0x4000〜0x5FFFの書き込み設定
○ポート0x6B:CPUアドレス:0x6000〜0x7FFFの書き込み設定
○ポート0x6C:CPUアドレス:0x8000〜0x9FFFの書き込み設定
○ポート0x6D:CPUアドレス:0xA000〜0xBFFFの書き込み設定
○ポート0x6E:CPUアドレス:0xC000〜0xDFFFの書き込み設定
○ポート0x6F:CPUアドレス:0xE000〜0xFFFFの書き込み設定


 ・SR以降のみに存在するI/Oポートです。
 ・SRモード(ポート0xC8:bit0=0)以外では動作しません。
 ・書き込みのみ。読み出しはできません(読み出すと0xFFになる)。


  書き込むデータは次の通り。


  bit7-4:割り当てるメモリの種別

   bit7-4=0000:内部RAM(VRAM)
   bit7-4=0010:外部RAM(拡張RAM)
   bit7-4=1011:外部ROM1(拡張ROM(nCS2):通常、0x6000〜0x7FFFに割り当てる)
   bit7-4=1100:外部ROM2(拡張ROM(nCS3):通常、0x4000〜0x5FFFに割り当てる)
   bit7-4=1101:CGROM
   bit7-4=1110:SYSTEMROM2(音声合成/漢字)
   bit7-4=1111:SYSTEMROM1(BASIC-ROM)
   それ以外は未使用


  bit3-1:割り当てるメモリの先頭アドレス(アドレスバスA15-13に出力する値)
  bit0:常に"0"



◎解説
 CPUからメモリなどにアクセスする際に、8Kバイトごとに自由に割り当てる事が出来る機能です(内部RAMは制限あり。後述)。
 SRモード専用の機能で、SRモード以外は使用できません。mk2/66モードのポート0xF0〜0xF2に似たような機能がありますが、ポート0xF0〜0xF2はバンク切り替えのイメージです。

 設定値の与え方がちょっと分かりにくい(特にbit3-1)ですので、具体的な例を挙げてみます。


 SYSTEMROM1(BASIC-ROM)の0x8000〜0x9FFFを、0x0000〜0x1FFFから読み出したい場合は、

 ・設定するポートは、0x0000から読み出すので、ポート0x60に書き込む。
 ・設定値bit7-4=SYSTEMROM1(BASIC-ROM)、1111
 ・設定値bit3-0=エリアの先頭に0x8000を割り当てるから、0x8=1000

  となって、ポート0x60に0xF8を書き込む。



 外部RAM(拡張RAM)の0x6000〜0x7FFFを、0x4000〜0x5FFFから書き込みたい場合は、

 ・設定するポートは、0x4000から書き込むので、ポート0x6Cに書き込む。
 ・設定値bit7-4=外部RAM(拡張RAM)なので、0010
 ・設定値bit3-0=エリアの先頭に0x6000を割り当てるから、0x6=0110

  となって、ポート0x6Cに0x26を書き込む。


 このメモリ割り当て機能ですが、回路的にはCPUアドレスのA15-13を入力として、新しくアドレスRA15-13を生成する回路と、チップセレクトを生成する回路になります。

 A15-13からRA15-13を生成する回路は、こんな感じです。

151121_01_メモリ割り当て1.jpg



 メモリアクセスに関しては、すべてRA15-13を使うのですが、内部RAM(VRAM)だけは、RA15-14とA13を使っています。多分バグだと思われます。

151121_01_メモリ割り当て2.jpg



 そのため内部RAM(VRAM)に関しては、以下のような動作になります。


○ポート0x60:CPUアドレス:0x0000〜0x1FFFの読み出し設定
○ポート0x62:CPUアドレス:0x4000〜0x5FFFの読み出し設定
○ポート0x64:CPUアドレス:0x8000〜0x9FFFの読み出し設定
○ポート0x66:CPUアドレス:0xC000〜0xDFFFの読み出し設定
○ポート0x68:CPUアドレス:0x0000〜0x1FFFの書き込み設定
○ポート0x6A:CPUアドレス:0x4000〜0x5FFFの書き込み設定
○ポート0x6C:CPUアドレス:0x8000〜0x9FFFの書き込み設定
○ポート0x6E:CPUアドレス:0xC000〜0xDFFFの書き込み設定

 内部RAM(VRAM)を割り当てる場合は、以下の値しか取れない。

 0x0000〜0x1FFF:書き込むデータbit7-0=00000000
 0x4000〜0x5FFF:書き込むデータbit7-0=00000100
 0x8000〜0x9FFF:書き込むデータbit7-0=00001000
 0xC000〜0xDFFF:書き込むデータbit7-0=00001100


○ポート0x61:CPUアドレス:0x2000〜0x3FFFの読み出し設定
○ポート0x63:CPUアドレス:0x6000〜0x7FFFの読み出し設定
○ポート0x65:CPUアドレス:0xA000〜0xBFFFの読み出し設定
○ポート0x67:CPUアドレス:0xE000〜0xFFFFの読み出し設定
○ポート0x69:CPUアドレス:0x2000〜0x3FFFの書き込み設定
○ポート0x6B:CPUアドレス:0x6000〜0x7FFFの書き込み設定
○ポート0x6D:CPUアドレス:0xA000〜0xBFFFの書き込み設定
○ポート0x6F:CPUアドレス:0xE000〜0xFFFFの書き込み設定

 内部RAM(VRAM)を割り当てる場合は、以下の値しか取れない。

 0x2000〜0x3FFF:書き込むデータbit7-0=00000010
 0x6000〜0x7FFF:書き込むデータbit7-0=00000110
 0xA000〜0xBFFF:書き込むデータbit7-0=00001010
 0xE000〜0xFFFF:書き込むデータbit7-0=00001110


 また、書き込むデータbit3-1が、RA15-13に出力されますが、内部RAM(VRAM)使用時はRA13は未接続のために、bit1は無視されます。


 上記制限のため、例えば

 CPUアドレス 0x0000〜0x1FFF に、内部RAM(VRAM)0x4000〜0x5FFF を割り当て
 CPUアドレス 0x2000〜0x3FFF に、内部RAM(VRAM)0x2000〜0x3FFF を割り当て
 CPUアドレス 0x4000〜0x5FFF に、内部RAM(VRAM)0x0000〜0x1FFF を割り当て
 CPUアドレス 0x6000〜0x6FFF に、内部RAM(VRAM)0x2000〜0x3FFF を割り当て

 はOK(複数から同じRAMに割り当ててもOK)なのですが、

 CPUアドレス 0x0000〜0x1FFF に、内部RAM(VRAM)0x2000〜0x3FFF を割り当て
 CPUアドレス 0x2000〜0x3FFF に、内部RAM(VRAM)0x0000〜0x1FFF を割り当て

 はNGです。

 また、内部RAMはVRAMとして使用されていますが、メモリ割り当てではあくまでもCPUからのアクセスアドレスが変わるだけなので、画面表示への影響はありません。

 さらにVRAMでビットマップモードを使用した場合は、メモリ割り当てでアドレス変換された後に、ビットマップ変換がされます。詳しくはSRグラフィックの説明の際に記載します。
posted by えすび at 16:09| Comment(0) | P6解析:CPU周り | このブログの読者になる | 更新情報をチェックする

2013年06月02日

タイマ割り込みの違い

  オフで話題になった件です。


  コナミのスクランブル(カートリッジ版)を実行させた場合、開始時に鳴る音楽のテンポが違うというものです。
  確かに、PC-6001初代機と、mk2ではテンポが違って、初代機に比べてmk2のテンポがかなり遅く感じます。

  BASIC-ROM の違いかとも思いましたが、エミュレータでは同じテンポで鳴ります(mk2と同じ、遅い方のテンポ)。
  FPGA版ではどうかと調べると、実機と同じように鳴りました。

  ただ、心当たりがないので、調べてみました。


  結論は、2ms タイマの割り込み間隔が違う(時がある)のが原因です。

  初代機で、スクランブルを動かした場合、通常 2ms 周期の割り込みタイマが 1ms 周期になります(多分、意図的に 1ms にしているんだと思います)、


  では、なぜこうなるか、ですが...

  実は、PC-6001初代機と、PC-6001mk2以降では、タイマOFF からタイマON にした直後の、最初の割り込みタイミングが違います。

2msタイマ


  mk2以降では、タイマON にした直後でも 2ms 後に割り込みが発生します。

  しかし、PC-6001初代機では、タイマON にした直後は、1ms 後に割り込みが発生します。
  回路のバグというか仕様というか...ディスクリート部品で回路を組んでいる関係上、簡略化した副作用です。


  スクランブルですが、割り込みルーチンは以下のようになっています。

 4AE0 PUSH AF
 4AE1 PUSH BC
 :
 4B1B LD A,03H
 4B1D OUT (0B0H),A
 4B1F DEC A
 4B20 OUT (0B0H),A
 4B22 EI
 4B23 POP AF
 4B24 RET

  割り込みの最後で、タイマON → OFF → ON として、タイマの再起動をしています。

  このため、大体1msの間隔で割り込みが入るようになっています。
 (実際には、割り込みルーチンの最後の方でタイマOFF/ON をしているため、もうちょっと長いです)


  mk2以降で同じようにするには、ポート0xF6 に、03H の代わりに 01H or 02H を出力すると、大体同じになるはずです(ソフトによります。スクランブルなら、02H ぐらいが適当なようです)。

posted by えすび at 15:10| Comment(6) | P6解析:CPU周り | このブログの読者になる | 更新情報をチェックする

2013年01月12日

uPD780未定義命令(1)

  これもツイッターで見つけた発言から。

>>TAPE版ロードランナーのローダーをちょっと読んでみた。唐突にEDH,C4Hとか現れるけど,それがなくてもプログラムとして成立してるっぽい。ウェイト?よくわかんね。


  未定義命令に関しては、ちゃんと解析する必要があるとは思います。

  昔と違ってロジアナが使えるので、解析はだいぶラクだとは思いますが。


  今の音声合成の解析が終わってからだな(^^;

posted by えすび at 13:53| Comment(7) | P6解析:CPU周り | このブログの読者になる | 更新情報をチェックする

2012年11月10日

入出力命令のバグ?

  Z80の入出力命令は、以下のようなものがあります。

IN A,(n) :A <- port (n)
IN r,(C) :r <- port (C)
INI :(HL) <- (C) , HL<-HL+1 , B<-B-1
INIR :(HL) <- (C) , HL<-HL+1 , B<-B-1 , B!=0なら繰り返す
IND :(HL) <- (C) , HL<-HL-1 , B<-B-1
INDR :(HL) <- (C) , HL<-HL-1 , B<-B-1 , B!=0なら繰り返す

OUT (n),A :port (n) <- A
OUT (C),r :port (C) <- r
OUTI :(C) <- (HL) , HL<-HL+1 , B<-B-1
OTIR :(C) <- (HL) , HL<-HL+1 , B<-B-1 , B!=0なら繰り返す
OUTD :(C) <- (HL) , HL<-HL-1 , B<-B-1
OTDR :(C) <- (HL) , HL<-HL-1 , B<-B-1 , B!=0なら繰り返す


  大抵の本には、こう書かれているはずです。

  ですが、Z80 のバグというか仕様で、一部の命令が上記の記載とは違う動きをします。


  その命令は4つで、動作は以下の通りです。

OUTI :B<-B-1 , (C) <- (HL) , HL<-HL+1
OTIR :B<-B-1 , (C) <- (HL) , HL<-HL+1 , B!=0なら繰り返す
OUTD :B<-B-1 , (C) <- (HL) , HL<-HL-1
OTDR :B<-B-1 , (C) <- (HL) , HL<-HL-1 , B!=0なら繰り返す


  Breg のデクリメントが、ポートへ出力するよりも速いんですね。


  別に影響がないように思えますが、実は大有りです。


  Z80 のシステムは、I/O領域へのアクセスは0x00〜0xFF の8ビットが普通なんですが、
 16ビット単位で行う事も可能です。

  上記命令でいうと、Creg を使って入出力をしているものに関しては、アドレスバスには Breg と Creg の内容の両方が出ています。上位8ビットが Breg で、下位8ビットが Creg です。

  通常のシステム(例えばP6)では、下位8ビットしか使わずに、8ビットでのI/O領域を構成しています。


  ただし、66 では、16ビットI/O を使っている箇所が一箇所だけあります。それは、ディスク用バッファの入出力です。


  ディスク用バッファは1Kバイトで、0x000〜0x3FF までのアドレスがあります。
  このアドレスにアクセスするためには、ポート0xD0〜0xD3 を使います。

  0xD0:0x000〜0x0FF
  0xD1:0x100〜0x1FF
  0xD2:0x200〜0x2FF
  0xD3:0x300〜0x3FF

  この時、バッファのアドレスの下位8ビットを表すのに、アドレスバスの上位8ビットが使われます。


  具体的には、

LD BC,012D0H
IN (C),A

  とすると、バッファアドレス0x0012 の内容を Areg に読み出すことができます。



  で、最初の話に戻ると、上記の OUTI/OTIR/OUTD/OTDR を使うと、Breg が先にデクリメントされるため、読み書きするアドレスが一つずれるという現象が起こります。


  Z80 では、他に割り込み系の命令にもバグがあったと思います。また詳しく調べたいと思います。


posted by えすび at 22:52| Comment(14) | P6解析:CPU周り | このブログの読者になる | 更新情報をチェックする

2012年08月23日

mk2のウエイト

  WAIT 関連ですが、やはりバグがありました。


  仕様は以下の通りです。

  I/Oポート 0xF3(初期値:0xC2)

  bit7: M1 サイクルで 1WAIT 挿入(1 で WAIT 挿入)
  bit6: ROMブロックで 1WAIT 挿入(1 で WAIT 挿入)
  bit5: RAMブロックで 1WAIT 挿入(1 で WAIT 挿入)

  bit4-0 は、割り込み関連です。

  初期値が、0xC2 なので、M1サイクルとROMブロックで 1WAIT 挿入されます。

  仕様がちょっとあいまいなんですが、bit7=1 の場合は、ROMかRAMかに関わらず、M1サイクルで 1WAIT 挿入されます。
  また、bit6=1 の場合は、ROM のプログラムのM1 サイクル、オペランドのリード、ROM からのリードなどで、1WAIT挿入されます。


  例えば、bit7-5 = 110 の場合、

  3A 00 FA LD A,(FA00H)

  この命令では、4+3+3+3 ですが、このプログラムが ROM の場合は、(4+1)+(3+1)+(3+1)+3 になります。このプログラムが RAM の場合は、(4+1)+3+3+3 になります(M1 のみ 1WAIT)



  で、バグです。

  1つ目は、bit7-5 = 001 つまり、RAMブロックのみ1WAIT を挿入した場合に、ROMブロックからリードすると、なぜか1WAIT されます(M1 サイクルに関しては確認出来ていません)。


  2つ目が問題なんですが、M1 サイクルを 0WAIT に出来ません。
  詳しく言うと、bit7-5 = 000、RAM領域での bit7-5 = 001 などを設定すると暴走します。

  ROM に対しては、遅いのでダメというのは分かるんですが、RAM はそれなりに速いはずなので、ちょっとヘンです。


  で、ロジアナで調べてみました。


wait.zip

  調査したプログラムです。内容は、ROMの内容0x0000-0x7FFF の値を足し算するだけです(それを16回繰り返します)。
  このプログラムでも、bit7-5 = 000、001 と設定すると、暴走します。


  結論から言うと、RAMへのアクセスが適切ではないため、WAIT なしでは動作しない状態になっていました。



WAIT実機

  ロジアナで取った波形です。上が 1WAIT、下が0WAIT です。それぞれ左の線が M1サイクルの始まり、右の線が M1サイクルの終わり(=RAMからのデータの取り込みタイミング)になります。

  RAM(DRAM)は、RAS が L になって、その後に CAS が L になってから、データが出力されます。

  上の 1WAIT では、CAS が L になってから、4MHz で 1.5CLK 、つまり375ns あります。
  下の 0WAIT では、CAS が L になってから、4MHz で 0.5CLK 、つまり125ns あります。

  0WAIT の場合、RAMの出力遅延とZ80のセットアップ時間の和が、125ns 以下になる必要がありますが、これが不足しています。
  そのため、0WAIT では、正常なデータを取り込めなくなり、暴走しているようです。


CRTコントローラ部(mk2)

  ここで、回路図の左上のFFを見てみると、CAS 信号は4MHz の立下りで叩かれているため、この時間だけ遅れている事がわかります。
  では、ここを何とかすると動くのでは?



  という事でやってみました。


16Mクロック生成部(mk2)

  これは、16MHz から4MHz を作っている回路ですが、4MHz の立下りで叩くのではなく、この16MHz の立下りで叩くと、16MHz で 1clk分、62.5ns ほど速く出せるようになります。
  DRAMの仕様を確認してみても、RAS に対して、CAS が速すぎる事もなさそうです(50ns ほどあればいいらしい)。

  回路としては、IC2 の 3pin に、現在入力されている 4MCLK の代わりに 16MCLK を入力します。16MCLK は、IC44 の 12pin から取ります。
  上記は、mk2 の時で、66 の場合は、IC16 の 1pin に、IC60 の 10pin を入力します。

  この改造のためには、1箇所パターンカット(もしくはICの足上げ)が必要になります。


  回路図としては、4MHz と書かれている所を 16MHz と変更するだけなんですけどね(^^;




  改造した結果です。


waitあり

  同じプログラムを動かした時のロジアナの波形(1WAIT挿入)です。上が改造前、下が改造後です。
  MEMC-DR-nCAS が少し速くなっているのがわかると思います。


waitなし

  同じプログラムを動かした時のロジアナの波形(WAIT挿入なし)です。上が改造前、下が改造後です。
  同じようにMEMC-DR-nCAS が少し速くなっているのがわかると思います。
  RAMからの出力の波形を取ってなかったんですが、かなり余裕があるのがわかると思います。


  実際のプログラムでどれだけ違うのか、AX-9 のフライトシミュレータで試してみました。




  まず、何もしない状態です(M1、ROM にウエイトあり)。




  次に、WAIT を ROM のみにしました(OUT&HF3,&H42)。
  ちょっと速くなってる?

  通常は、ROM内ルーチンよりユーザプログラム上を走っている事が多いので、これでも十分効果があるはずです。




  ROM のWAIT をなくす事は出来ないので、ROM のデータをRAMにコピーして、RAMで動かしてみました。


romcpy.zip

  ちなみにこれが ROM のデータをRAMにコピーするプログラムです。


  特定の箇所で、描画時間を測定してみました(14フレーム分)。

  何もしない状態:14.1 秒
  ROMのみWAIT挿入:12.2 秒
  WAITなし:12.2 秒

  手動でストップウオッチなので、多少の誤差はありますが、ROM のみWAIT挿入でも速くなっています。
  M1サイクルのWAIT が無くなるので、当然の結果ではあります。


  ちなみに改造をしても、0xF3 のWAIT関連ビットを変えなければ、(当然)今まで通りの動作をします。

  ※改造は自己責任で行って下さい。


posted by えすび at 21:35| Comment(0) | P6解析:CPU周り | このブログの読者になる | 更新情報をチェックする

2012年06月08日

ディスク関連(8)

 外付けドライブが異常に遅いのは、データ転送時の問題のようでした。
 でもまだ遅い...

 というわけで、速度を比較してみました。
 比較用に、Bookwormさんに作ってもらった速度計測用のプログラムを一部修正して使いました。


 フロッピーからの読み出しですが、P66内蔵ドライブだと以下のような流れになります。

1)CPUから、モータONのコマンドを出す
  ↓
2)モータON
  ↓
3)CPUから、μPD765Aに SEEK コマンドを発行
  ↓
4)μPD765A は、FDDに対して、目的のシリンダ(≒トラック番号)にヘッドを動かすように指示する
  ↓
5)CPUから、μPD765Aに SEEK コマンドの実行結果を聞く(SENSE INTERRUPT STATUSコマンド発行)
  ↓
6)μPD765A が、実行結果を報告する
  ↓
7)CPUから、μPD765Aに 目的のセクタの読み出しを指示する(READ DATA コマンドを発行)
  ↓
8)μPD765A は、FDD から入力される「フロッピーのデータ」から、目的のセクタを探す
  ↓
9)μPD765A は、リード用バッファに対して、目的のセクタのデータを書き出す
  ↓
10)書き出しが完了したら、FDINT を出力してCPUに通知する
  ↓
11)CPUは、リード用バッファに入っているデータをメインメモリに転送する

 となります。

 最初の1)2)は、実機では時間がかなり掛かりますが、FPGA版では無視です。
 3)〜6)も、実機では時間が掛かりますが、FPGA版では一瞬です。

 また、1)〜6)は、連続で同一シリンダ(トラック)を読む場合では、この時間は無視できます。


 速度測定プログラムで測定したのは、7)〜11)の時間になります。

 元のプログラムや、ROM内ルーチンでは、途中に割り込み禁止が入っていたのですが、これは外しました。
 (割り込み禁止が入っている理由ですが、μPD765A の仕様で、何μS までに処理を完了させなければならないとかいうのがあるみたいです)


 で、測定した結果です。

 ・実機

  手で何度も測定した場合、100〜270ms ぐらいと数字がばらけます。
  BASIC でループさせた場合、106〜112ms になりました。

  また、セクタ番号を1と16をやってみましたが、傾向は同じでした。


 ・実機の考察

  手で測定した時に数字がばらけるのは、その時のヘッドの位置と読みたい場所の関係が毎回違うからだと思われます。

  たまたま、ヘッドと読みたい場所が近い場合は時間が短く、遠い場合は時間が長いようです。
  BASIC でループさせた場合は、この距離が一定になるから、数字がばらけなかったようです。


  ヘッドと読みたい場所が最悪の時の時間は、ディスクが1周する時間と同じですので、

  1/ 300rpm = 1/(300/60) = 200ms

  で、ディスクの場所により、0〜200ms の時間待たされる事になります。
  (300rpmは、もしかしたら600rpmかも)


  実測値と比較すると、リードするのに100ms、読み出すのを待っている時間0〜200ms で大体合っているような感じです。



 ・FPGA版

  構造上、セクタ番号で掛かる時間が違うようになっています
  セクタ1が一番速く、セクタ16が一番遅くなります。

  セクタ1で、56〜60ms、セクタ16で140〜154msでした。


  遅いのは遅いんですが、実機と比べると速いので、ヨシとする事にします。



posted by えすび at 00:05| Comment(0) | P6解析:CPU周り | このブログの読者になる | 更新情報をチェックする