2021年3月30日火曜日

Zilog Z80280

 Z8000へのPCC移植がちょっと行き詰まってしまい、気分転換にはんだ付けがしたくなってきました。今回の目標はZilog の Z280を動かすことです。


この石は中国から購入したのですが、ほんまもんなのかがわかりません。時間かけたあげく無駄に終わる可能性もあります。前回のZ8002も中国から購入したのですが、(本当に10MHz品かは別として)こちらは動作しました。偽物を掴まされてたとしても、それはそれで楽しいので、手をつけてみます。

Z280は、Z80とコードレベルで互換性がある16bit CPUです。単純にCPUなわけではなく、MMUやDMA、タイマ、シリアルポートなどを集積したかなり豪華な構成になっています。メモリスペースはMMUを通して、64kByteから16MByteまで拡張されています。外部バスは、16bitのZ-BUSモードか8bitのZ80 Busを選べるようになっています。

もちろん、Z280の性能を活かし切るにはZ-BUSで使うことになるのですが、配線量が増えるし、偽物だったときの精神的ショックを抑えるため、まずはZ80 Busで動かしてみたいと思います。


2020年12月26日土曜日

Z8000にPortable C Compilerを

かなりブログの投稿をさぼっていたのですが、PCCをZ8000に移植(対応?)させようと格闘しています。 現在挑んでいるのは、最新のPCC1.1.0ではなく、70年代のV7 UNIXに含まれていたANSI C 以前のPCCです。 
作業を単純化するため、ノンセグメントモードだけに対応しているので、実質Z8002用です。 過去には、i8086, MC68000 と共にZ8000版も有ったようなのですが、ネット上を探し回りましたが見つからず、現在では失われてしまっているようです。 まあ、40年もまえのことなので仕方がないですね。  
現在は、アーキテクチャの似ているPDP-11版をベースに、Z8000のコードを吐けるよう段階的に書き換えていっています。  

下は引数を2つ取って足し算をして返すだけの関数をコンパイルした例です。 

 C言語
add(a, b) int a, b;
{
        a += b;
        return a;
}
Z8000アセンブリ
	.data
	.text
	.globl	_add
_add:
	call	csv
	sub	r15,#F1
	ld	r2,4(r14)
	add	r2,6(r14)
	ld	4(r14),r2
	ld	r2,4(r14)
	jp	cret
	jp	cret
	.F1 = 0.
	.data
最適化は全くされていないので無駄が多いコードですが、正しくアセンブリを出力できるようになってきています。
コンパイラの作業が終わっても、次はアセンブラとリンカが必要なので、まだまだ時間が掛かりそうで気が遠くなります。いつか公開できる日が来ることを祈りつつ。

2020年9月30日水曜日

Z8530 SCC

 Z8530 SCCを制御するコードも上げておきます。

先ずは初期化のコードです。送受信の割り込みが処理できるように初期化しています。この石もかなり曲者で初期化手順を間違うとうまく動きません。

発生させる割り込み込みベクタは0x10からで、チャンネルAの送信が0x18で、受信が0x1cになります。

	.equ	SCCAC, 0x0009
	.equ	SCCAD, 0x000D

init_scc:
	ld	r2, #(scccmde - scccmds)	! initialize Z8530
	ld	r3, #SCCAC
	ld	r4, #scccmds
	otirb	@r3, @r4, r2
	ret

scccmds:
	.byte	9, 0xc0		! Reset
	.byte	4, 0x44		! x16, 1stop-bit, non-parity
	.byte	3, 0xe0		! Receive  8bit/char, rts auto         
	.byte	5, 0xe2		! Send 8bit/char, dtr rts

	.byte	2, 0x10		! Interrupt vector
	.byte	1, 0x12		! Interruprt on Rx All character and Tx Int
	.byte	9, 0x09		! MIE, VIS, Status=Low 

	.byte	11, 0x50	! BG use for receiver and transmiter
	.byte	12, 30		! 4800bps at 5MHz clock
	.byte	13, 00
	.byte	14, 0x02	! PCLK for BG
	.byte	14, 0x03	! BG enable

	.byte	3, 0xe1		! Receiver enable
	.byte	5, 0xea		! Transmiter enable

scccmde:

割り込みハンドラーは、次のようなコードです。リングバッファ処理部分は端折ってあります。Z8536と同じで割り込み処理の終了処理を正しく行わないと、優先順位の低い割り込みがかからなくなったりします。

scc_rxint:				! Rx InterruptHandler
	push	@r15, r0
	push	@r15, r1
1:	
	inb	rl0, #SCCAC
	andb	rl0, #0x01
	jr	z, 1b 
	inb	rh0, #SCCAD		! Read a data from Rx buffer
	
    !
	!
    
	ldb	rl0, #0x38		! Enable IUS
	outb	#SCCAC, rl0
	pop	r1, @r15
	pop	r0, @r15
	iret

scc_txint:				! Tx Interrupt Handler
	push	@r15, r0
	push	@r15, r1

	!
    !
    
	outb	#SCCAD, rl0 ! Write a data to Tx buffer
	
	ldb	rl0, #0x28		! Clear Tx interrupt pendinng 
	outb	#SCCAC, rl0
	ldb	rl0, #0x38		! Enable IUS
	outb	#SCCAC, rl0
	
    pop	r1, @r15
	pop	r0, @r15
	iret

2020年9月12日土曜日

Z8536 CIO

 Z8536の初期化手順と割込み処理のメモです。カウンタタイマ3で10mS周期の割り込みをかけます。

Z8536にはRESETピンがなく、ハードウェアリセットはRDピンとWRピンを同時にLにすることで行います。今回はハードウェアを手抜きしているので、ソフトウェアでリセットしています。A0, A1ピンをHにしてリードし、つづいて、レジスタ0に1を書き込んだのちレジスタ0に0を書き込みます。これで初期化されます。

レジスタのアクセスは、A0, A1ピンをHにしアクセスするレジスタ番号を書き込み、つづいて目的のレジスタに書き込みか読み込みをします。Z8536を初期化するのにレジスタ設定の順番を変えたりすると、なぜか動かなくなったりします。正しい手順はよく理解できていないのですが、手探りで見つけた手順が下のコードです。


	.equ	CIOPRTC, 0x0011
	.equ	CIOPRTB, 0x0015
	.equ	CIOPRTA, 0x0019
	.equ	CIOCTRL, 0x001d


init_cio:
	ld	r1, #CIOCTRL
	inb	rl0, #CIOCTRL
	lda	r2, ciocmds
	ld	r3, #(ciocmde - ciocmds)
	otirb	@r1, @r2, r3
	ret

ciointr:
	push	@r15, r0	! レジスタ保存
	push	@r15, r1

	! 割り込み処理
	!
	!
	
	ldb	rl0, #0x0c
	outb	#CIOCTRL, rl0
	ldb	rl0, #0x24
	outb	#CIOCTRL, rl0	! Clear IP and IUS
	
	pop	r1, @r15	! レジスタ復帰
	pop	r0, @r15
	iret

!------------------------------------------------------------------------------

ciocmds:
	.byte	0x00, 0x01		! Reset
	.byte	0x00, 0x00		! Clear reset
	.byte	0x01, 0x00		! Reset PortC and C/T3
	.byte	0x01, 0x10		! Enable PortC and C/T3
	.byte	0x1e, 0xc2		! Continuous, Ext Output, Square wave
	.byte	0x1a, 0x30		! Set timer constant
	.byte	0x1b, 0xd4		! Set timer constant
	.byte	0x00, 0x84		! MIE and VIS
	.byte	0x04, 0x00		! Set Interupt vector
	.byte	0x0c, 0xc0		! Set Interupt Enable bit
	.byte	0x0c, 0x06		! Gate and Triger
ciocmde:


割り込み処理は、最後にレジスタ12のIPとIUSをクリアして戻れば、続けて割り込みが発生します。これを忘れると、割り込みが単発になったり、他のチップからの割り込みが発生しなくなります。

Z8530もそうでしたが、Zilogの周辺チップはプログラムが難しい印象です。さらにデータシートが不親切で、読んだだけでは理解できないことが多々あります。ネット上にもあまり情報がないので、手探りがで動かすことになるのですが、動いた時の高揚感は格別です。

2020年9月6日日曜日

Z8002にMMUを その3

今回作成したMMUの回路図が下です。

高速SRAM, マルチプレクサ、双方向バッファ、CPLDからなっており、CPLDが各ICの制御信号を作っています。TTL-ICを組み合わせて制御信号を作るのは面倒なのと、うまく動かなかったときに、CPLDだと回路の変更をせずにロジックを書き換えるだけで対処できます。

CPLDの定義ファイルのMMUに関連する部分の抜粋です。
IOREQ = ST0 # !ST1 # ST2 # ST3;

DI = !ST3;
MMUSEL = !A15 # !A14 # IOREQ;
MAPSEL = MMUENA & MMUSEL;
MMUBUFE = MMUSEL # DS;
MAPRD = (MMUSEL # !RW # DS) & !IOREQ;
MAPWR = MMUSEL # RW # DS;
1個のCPLDでメモリアクセス用の信号も作ったりしているので、I/Oをほぼ使い切ってしまっています。
アドレスマップの高速SRAMは、I/O空間でアクセスでき読み書きができます。アドレスは次のようになっています。

- no title specified

I/O Addr

N/S

D/I

Page No.

C001

S

I

0

:

:

:

:

C01F

S

I

F

C021

N

I

0

:

:

:

:

C03F

N

I

F

C041

S

D

0

:

:

:

:

C05F

S

D

F

C061

N

D

0

:

:

:

:

C07F

N

D

F

    
    
S --- System, N --- Normal (User), D --- Data, I --- Instruction

システムがリセットした時には、アドレスマップを初期化したのちMO端子をアクティブにすることでMMUが機能します。それまでCPUは、RAMの先頭ページ(4kバイト)しかアクセスできません。

2020年7月30日木曜日

Z8002にMMUを その2

今回作るMMUは、高速SRAMをマッピングテーブに使った回路になり、下の図のような構成になります。


Z8002のアドレス下位12ビットは、そのまま物理アドレスの下位12bitとしてメモリに渡します。Z8002のアドレス上位4bitは高速SRAMのアドレスに入れ、読み出されるデータ8bitを物理アドレスの上位8bitとします。これで合計20bit( 1Mバイト分)のアドレスに変換します。

アドレス変換を行うには、高速SRAMにマッピングデータを書き込む必要があるので、マルチプレクサとバッファをつけます。高速SRAMは0xC000からのI/O空間に置きアクセスします。マルチプレクサは、変換する仮想アドレスの上位4ビットとI/Oアクセスするアドレスを切り替えるために使います。バッファは、高速SRAMをデータバスに接続するために必要です。

本当は、アドレスの上位4ビットをバイパスするために、高速SRAMとメモリの間にさらにマルチプレクサを追加したいのですが、部品と配線量が増えるので見送ります。これはCPUリセット後に高速SRAMを初期化するまでの間、MMUを無効にしておくために必要なのですが、単純に高速SRAMを非アクティブにしおいて データバスをプルダウンすることにします。このため上位8ビットが0に固定され、MMUが初期化されるまでの間はメモリの先頭の4kバイトしかアクセスできなくなってしまいます。

いま気づいている点は、
  • ページがマップされていない状態を設定できない
  • ページごとに書き込み禁止を設定できない
  • ページに書き込みがあったか記録できない
などですが、実現するには高速SRAMを追加する必要があります。これらの機能がどれくらい必要なのか判断がつかないので、MMUは最小限の構成にしておいて、とりあえず動かすことを優先します。


2020年7月12日日曜日

Z8002にMMUを その1

MMUについて考えていきます。

Z8001にはZ8010というMMUが準備されていますが、Z8002にはつなげられないため、必然的に自作することになります。今回はUNIX V6を動かそうとしているので、DECのPDP-11のメモリ管理機構を参考にします。ネットの情報と「はじめてのOSコードリーディング」を読んだところでは、ざっと下のような感じです。
  • 64kバイトの物理アドレスは、8つのセグメントに分割されている
  • セグメントの最大サイズは8kバイト
  • 各セグメントは、64バイト単位でサイズが可変できる
  • 各セグメントは、物理アドレスの64バイト境界にマップできる
  • カーネルとユーザで空間を分離できる
  • 命令とデータの空間が分離できる
  • セグメント毎に読み込み専用に指定できる
完全に同じものを作るのは難しそうなので、次のように単純化します。
  • セグメントサイズは固定
  • セグメントの物理アドレスへのマッピングはセグメントサイズ境界に揃える
  • 読み込み専用指定は実装しない
  • カーネルとユーザで空間は分離する
  • 命令とデータで空間は分離する
セグメントのサイズは、4kバイト固定にします。マッピングテーブルに高速SRAMを使ったアドレス変換回路を組むのですが、高速SRAMのデータバスは8ビットなので、セグメントサイズを4kバイトにすると、上位8ビット + 下位12ビットでちょうど1Mバイトのメモリを扱うことができます。

セグメントのマッピング位置をセグメントサイズ境界に揃えると、メモリの使用に無駄が多くなりそうですが、PDP-11のように細かくしようとするとセグメント情報を記録するマップが大きくなってしまいます。また、加算回路が必要になるためアドレスが確定するまでの遅延が大きくなり配線量も増えるので諦めます。その分、セグメントサイズを4kバイトに小さくすることにします。

読み込み専用指定は、テキストセグメントへの書き込み禁止などの保護のために使うのでしょうか。今回は設定できなくても特に問題はないと思うので省略します。

カーネルとユーザ空間の分離は、Z8000がシステムとノーマルモードを持っており、どちらで動作しているか信号を出しているので実装できそうです。

命令とデータの空間分離は、Z8000では64kバイトしかアドレス空間がないので、プロセスが使えるメモリを増やすために有用です。これも信号が出ているので実装してみます。