tunozemichanの日記 / tunozemichan's diary

SORD社のコンピューターM68やM68MXの解析についての備忘録です。This blog is a memorandum about the analysis of SORD's computers M68 and M68MX.

SORD M68 Keyboardの解析(その3)

何年間も放置していたSORD M68のキーボードの回路についての解析を始めました。まだ、回路全体を明らかにできていませんが動作の概要は知ることができました。M68のキーボードは1980年代のPCに典型的なキースキャン方式で、キーマトリクスの押下情報(パラレル)を8bitのシリアルに変換して、M68に送っていると思われます。具体的な信号の内容はこれからロジックアナライザで調べます。特殊な転送方式ではありませんので、PS/2やUSBへの変換アダプターを作成することもできると思います。

回路図(途中まで)

 

MSXをネットワークにつなぐ(ObsoNET-reloadedイーサーネットカードの作成)

Z80とイーサーネット

Z80マシンであるSORD M23をLANにつないでみたいという願望があり、調べてみるとおなじくZ80 マシンであるMSXにはObsoNETというイーサーネットカードがあることを知りました。手元にMSXがあるので、さっそく作ることにしました。今回作成したのはObsoNET-reloadedです。

準備

obsonet reloadedをcloneします。


github.com

 

hardwareフォルダの下のgerberの中にあるobsonet_1-reloaded.zipをJLCPCBに発注しました。

パーツを集める

BOMファイルを見て、パーツを集めます。主要パーツは以下で入手しました。

 

RTL8019AS
秋月電子で入手しました。
「LANコントローラ RTL8019AS パルストランス付」
https://akizukidenshi.com/catalog/g/gI-14252/

 

74HC133
ヤフオクで入手しました。Aliexpressにもあります。
https://ja.aliexpress.com/item/1005005744549216.html?spm=a2g0o.order_list.order_list_main.31.6553585adQQdAa&gatewayAdapt=glo2jpn

 

GAL16V8D
手持ち品です。Ebay、Aliexpressにもあります。

 

M29F400M
Aliexpressで入手しました。安いですが足が曲がっていたり、ちゃんと動くかわからない点が不安要素です。
https://ja.aliexpress.com/item/4000117028836.html?spm=a2g0o.order_list.order_list_main.5.6553585adQQdAa&gatewayAdapt=glo2jpn
これを書き込めるROMライターがあれば、一度書き込めるかチェックしてからはんだ付けしたほうが良いと思います。


93C46
秋月電子で入手しました。MACアドレスを書き込んだものが売っていますが、フォーマットが違うので空の物を買うほうが安いです。
「3線式シリアルEEPROM AT93C46D-PU」
https://akizukidenshi.com/catalog/g/gI-02023/

 

パルストランス
Aliexpressで入手しました。秋月でRTL8019ASを買うと付いてくるので、それでも良いと思います。私は最初の製作だったのでなるべくBOMの記載通りにしたくて買いました。
https://ja.aliexpress.com/item/1005001701460892.html?spm=a2g0o.order_list.order_list_main.25.6553585adQQdAa&gatewayAdapt=glo2jpn


表面実装用スイッチ
秋月で入手しました。
【P-15969】 表面実装用タクトスイッチ TS-06104

 

LED
秋月で入手しました。
【I-06413】 3mm緑色LED 525nm OSG58A3131A(10個入)
【I-06412】 3mmオレンジ色LED 605nm OS5OAA3131A(10個入)

 

水晶発振器
秋月で入手しました。BOMと同じものは入手できませんでしたが、特に問題なく動作しています。
【P-08673】 クリスタル(水晶発振子) 20MHz

 

RJ-45コネクタ
マルツオンラインで入手しました。秋月で売っているRJ-45コネクタはピン間隔が異なり使用できません。
CONN MOD JACK 8P8C R/A SHIELDED【5555153-1】
https://www.marutsu.co.jp/pc/i/10830808/

 

抵抗、コンデンサなど
Aliexpressで入手できます。サイズはBOMに書いてあります。


作成の注意点
ほとんどありません。部品点数も少ないのですぐ組み立てられます。RTL8091ASはD型のこて先でやると滅茶滅茶簡単にはんだ付けできます。

一番の懸念はAliexpressで入手した29F400Mが不良品である可能性がある点です。はんだ付けしたあとで、剝がすのはめちゃめちゃ大変です。一度、ROMライターで書き込めることを確認してからはんだ付けしたほうが確実です。

93C46へのMACアドレスの書き込みは、このサイトに詳しいです。

theretrohacker.com


MAC Address Generatorがあるので、MACアドレスを持ってなくても問題ありません。

74HC133の取り付けはICソケットを使うと、MSXのカセット挿入口に干渉する可能性があります。ROMライタのICチェックで正常であることを確認して、
直付けが良いようです。

 

ObsoNET-reloadedイーサーネットカード表面

 

ObsoNET-reloadedイーサーネットカード裏面

 

 

完成後の作業(今回はMSX-DOS1を使いました)

RAM128kByte以上のMSXが必要です。

MSX-DOS1のディスクに、obsonet-reloadedのsoftwareフォルダの下にあるbiosフォルダ内のObsoNET Flash ROM Loader 1.0.zipを解凍して中のonetfrl.comをコピーします。さらに、bios_1.3フォルダのbios.rom、bios.datをコピーします。

 

MSXの電源を入れて、MSX-DOS1を起動します。

 

> onetfrl bios.rom

 

として、ObsoNETカードのフラッシュROMにBIOSを書き込みます。

 

フラッシュROMへの書き込み

 

うまくいかなかった場合、カードのはんだ付けの失敗が疑われます。しかし、フラッシュROMが不良品である可能性もあります。書き込みに成功したら、一度リセットします。

起動画面にObsoNETのバナーが出れば、動く状態にあると思われます。

MSX-DOS1起動後に、以下のコマンドを入力すれば、DHCPサーバーからIPを借りてきてネットを使用できる状態になっています。

> msr i
> inl2 i

msr.comのコマンドはMSX-DOS1のときのみ使用するようです。

msr.com、inl2.comの実行

ちなみに、自分のIPなどは

> inl2 s

というコマンドで得られます。

 

google.comにPingを打った様子です。

pingをwww.google.comに打った様子

 

SHARP X68000にCP/M-68Kを移植する(その5:BIOSアップデート(Ver.1.0.6))

X68000CP/M-68kのBIOSをアップデートしました。以下の2点を修正しました。

  • シフトキーなどメタキー入力の挙動修正
  • CTRL-Cキーの挙動動作

 

アップデート済みのCP/M-68KディスクイメージをGoogleDrivに置きました。

drive.google.com

 

 

通常使用に欠かせない修正となりました。これでキー入力周りは完成かと思います。

少し修正内容を書いておきます。

 

シフトキーなどメタキーの入力は、同時押しの処理は必要ではなく、キーバッファから1文字読み出して、メタキーだったら即捨てる(何も処理せず、IOCSのKEYINPをコールするところにジャンプする)ことで正しく動作しました。

 

CTRL-Cは、CP/M-68kでも様々なところで使われるキーバインドで、これが処理できないのは厳しかったのですが、CTRL-Cの例外処理であるTrap#13をCP/M-68kが設定するコードになっていたので、X68000の起動時の設定をそのまま利用するように変更したところ正常に動きました。

 

修正箇所だけアップしようかと思いましたが面倒なので、全コードアップします。

;*****************************************************************
;*      CP/M-68K BIOS                                           *
;*  Basic Input/Output Subsystem                                *
;*  For SHARP X68000 with floppy disk controller                *
;*****************************************************************

_ccp:   equ $150b8

IOCS        equ $0f
B_KEYINP    equ $00
B_KEYSNS    equ $01
B_SFTSNS    equ $02
B_BITSNS    equ $04
B_PUTC      equ $20
B_PRINT     equ $21
B_READ      equ $46
B_WRITE     equ $45
B_CONSOLE   equ $2E
B_CLS_ST    equ $2A

nfuncs  equ $17

maxdsk  equ $2      ; このBIOSでは2台のフロッピーディスクをサポートしている
dphlen  equ $1a     ; 1A=26 / disk parameter headerの長さは26
xlt0 equ 0

code    equ 1
data    equ 2
bss     equ 3

    section code

    org $1B000

_init:
    move.l  #traphndl,$8c   ; set up trap #3 handler

;
; 表示画面を80桁×25行に変更
;
    movem.l d0-d2,-(sp) ; d0,d1,d2をpush
    move.l  #$FFFFFFFF,d1
    move.l  #$004F0017,d2 ; 80(-1)×24(-1)
    move.l  #B_CONSOLE,d0
    trap    #IOCS

; 全画面をクリア
    move.b  #$2,d1
    move.l  #B_CLS_ST,d0
    trap    #IOCS

; オープニングメッセージを表示
    movem.l (sp)+,d0-d2 ; d0,d1,d2をpop
    move.l  #initmsg,a6
    bsr     prtstr

    clr.l   d0      ; log on disk A, user 0
    clr.l   write_flag
    clr.b   track
    move.b  #$ff,read_buffer_track  ; read bufferを不活化
    move.b  #$ff,write_buffer_track ; write bufferを不活化
    rts

traphndl:
    move.l  #biosbase,a0
    cmpi    #nfuncs,d0
    bcc     trapng
    lsl     #2,d0
    move.l  0(a0,d0),a0
    jsr (a0)
trapng:
    rte

biosbase:
    dc.l  _init
    dc.l  wboot
    dc.l  constat
    dc.l  conin
    dc.l  conout
    dc.l  lstout
    dc.l  pun
    dc.l  rdr
    dc.l  home
    dc.l  seldsk
    dc.l  settrk
    dc.l  setsec
    dc.l  setdma
    dc.l  read
    dc.l  write
    dc.l  listst
    dc.l  sectran
    dc.l  setdma
    dc.l  getseg
    dc.l  getiob
    dc.l  setiob
    dc.l  flush
    dc.l  setexc

;   nfuncs=(*-biosbase)/4 ; BIOSの関数の数

wboot:
    bsr flush
    move.b #$ff,read_buffer_track
    move.b #$ff,write_buffer_track
    jmp _ccp

constat:
    moveq.l #B_KEYSNS,d0
    trap    #IOCS
    rts

conin:
    moveq.l #B_KEYINP,d0
    trap    #IOCS

;   move.l  d0,keyboard_check
    move.l  d0,d1
    andi.l  #$0000FF00,d1
    cmp.l   #$00007000,d1   ; SHIFTキーの押下情報を無視
    beq     conin
    cmp.l   #$0000F000,d1   ; SHIFTキーの押下情報を無視
    beq     conin
    cmp.l   #$00005D00,d1   ; CAPSキーの押下情報を無視
    beq     conin
    cmp.l   #$00007100,d1   ; CTRLキーの押下情報を無視
    beq     conin
    cmp.l   #$0000F100,d1   ; CTRLキーの押下情報を無視
    beq     conin

;   move.l  d0,d1
;   andi.l  #$000000FF,d1
;   cmp.l   #$0000001B,d1   ; ESCキーが押された場合はエスケープシーケンスコード処理に移行する
;   beq     escape_sequence

    andi.l  #$000000ff,d0 ; IOCSの返す値からASCIIコードを抽出する
    rts


;escape_sequence:
;   moveq.l #B_KEYINP,d0
;   trap    #IOCS
;
;   andi.l  #$000000FF,d0
;   cmp.l   #$0000002A,d0   ;   "*"
;   cmp.l   #$00000061,d0   ;   debugの為に"a"にしてある。
;   move.l  d0,d1
;   andi.l  #$0000FF00,d1
;   cmp.l   #$00007000,d1   ; SHIFTキーの押下情報を無視
;   beq escape_sequence

;   cmp.l   #$00000054,d0   ; "T"
;   beq     es_delete_line

;   bra     conin

;es_delete_line:
;   ; カーソル位置から右の行を削除する。
;   movem.l d0-d1,-(sp) ; d0,d1をpush
;   move.b  #$0,d1
;   move.l  #B_CLS_ST,d0
;   trap    #IOCS
;   movem.l (sp)+,d0-d1 ; d0,d1をpush
;   bra     conin

;es_clear_screen:
;   ; 全画面をクリア
;   movem.l d0-d1,-(sp) ; d0,d1をpush
;   move.b  #$2,d1
;   move.l  #B_CLS_ST,d0
;   trap    #IOCS
;   movem.l (sp)+,d0-d1 ; d0,d1をpush
;   bra     conin


conout:
    moveq.l #B_PUTC,d0
    trap    #IOCS
    rts


prtstr:
    move.l a6,a1
    moveq.l #B_PRINT,d0
    trap #IOCS
    rts

lstout:
pun:
rdr:
    rts

listst:
    move.b  #$0,d0
    rts

;
; Disk Handlers for X68000(uPD72065) IOCS version
;

home:
    clr.b   track
    rts

getseg:
    move.l  #memrgn,d0  ; return address of mem region table
    rts

getiob:
    rts

setiob:
    rts

seldsk:
;   select disk given by register d1.b
    moveq   #0,d0
    cmp.b   #maxdsk,d1  ; 2台を超えてたら、そのままエラーにする
    bpl     selrtn      ; if no, return 0 in d0 / if plus then illegal drive no.

    move.b  d1,select_drive ; 正しいドライブ数なら、d1をselect_driveにセーブする
    move.b  select_drive,d0
    mulu    #dphlen,d0  ; dphlen(1Ah) * d0
    add.l   #dph0,d0    ; point d0 at correct dph
selrtn:
    rts

settrk:
    move.b  d1,track    ; update current track
    rts

setsec:
    move.b  d1,sector
    rts

sectran:
;   translate sector in d1 with translate table pointed to by d2
;   result in d0
    move.l  d1,d0   ; 今回はセクタ変換(セクタスキュー)をしないので、d1をそのままd0に入れる
    rts

setdma:
    move.l  d1,dma ; CP/MのDMAはシステムが読み書きするアドレスを指している。いわゆるDMAとはもはや無関係
    rts

;
; READルーチン
;

read:
; Read one sector from requested disk, track, sector to dma address
; Retry if necessary, return in d0 00 if ok, else non-zero
    bsr     read_disk
    move.l  #$0,d0 ; エミュレータ、X68000Zは常に、この処理は成功する
    rts

;
; read 128 byte from read_buffer
;

read_disk:
    move.b  select_drive,d0
    cmp.b   read_buffer_drive,d0
    bne     read_disk3      ; select drive != current drive ならDISKを読み、read_bufferをCP/MのDMAの指すアドレスにコピーする
    move.b  track,d0
    cmp.b   read_buffer_track,d0
    beq     read_disk1      ; track == read_buffer’track ならread_bufferを読みCP/MのDMAの指すアドレスにコピーする
read_disk3:
    bsr     read_track
;   bcc     $+4         ; エミュレータ、X68000Zは常に、この処理は成功する
;   rts
read_disk1:                 ; read_bufferからデータを読み、CP/MのDMAの指すアドレスにコピーする
    clr.l   d0
    move.b  sector,d0
    addq.b  #1,d0       ; CP/Mはなぜか理論セクタを0から始める。足して引くという意味のないことをしている
    subq.b  #1,d0       ; そのうち消す予定
    lsl.l   #7,d0       ; 128 * sector ; 該当するsectorがread_bufferのどこにあるか計算する
    lea     read_buffer,a0
    adda.l  d0,a0
    movea.l dma,a2
    moveq   #128,d0
read_disk2:             ; 該当するsectorが記録されているread_bufferの128バイトをCP/MのDMAの指すアドレスにコピーする
    move.b  (a0)+,(a2)+
    subq.b  #1,d0
    bne     read_disk2
    clr.l   d0
    rts
read_track:
    move.l  #$03000001,d2   ; sector len:3(1024), side = 0, sector = 1
    move.b  track,d4
    move.b  d4,read_buffer_track    ; read_bufferがどのトラックを記録しているのかを更新する

;   Side setting
    andi.l  #1,d4       ; d4 is side(0/1)
    lsl.l   #8,d4       ; left 8bit shift / side:bit 15-08

;   Track setting
    clr.l   d5
    move.b  track,d5
    lsr.b   #1,d5       ; d5は物理トラック番号

;   Drive numver setting
    clr.l   d6
    move.b  select_drive,d6 ; d6はドライブ番号
    move.b  d6,read_buffer_drive ; read_bufferがドライブ番号(のトラック番号)を記録しているのかを更新する
    lsl.l   #8,d6

;   DO READ
    clr.l   d0
    move.w  d5,d0       ; d5 is physical track number
    swap    d0          ; track:bit23-16
    add.l   d0,d2       ; set physical track no.
    add.l   d4,d2       ; set side no.
    move.l  #$9000,d1   ; 2HD
    or.l    d6,d1       ; drive number
    ori.w   #$0070,d1   ; 0111_0000: MFM, RETRY, SEEK
    move.l  #$2000,d3   ; size 8 * 1024
    move.l  #read_buffer,a1 ; to memory address

    moveq.l #B_READ,d0
    trap    #IOCS
    rts

;
; WRITEルーチン
;

write:
    bsr     write_disk
    move.l  #0,d0 ; emulator says always success
    rts

;
;   write 128 bytes to disk
;

write_disk:
    move.b  d1,write_type   ; d1.bをwite_typeにセーブする。これはdirectory書き込みの時は1になる。通常書き込みの時は0になる
    move.b  select_drive,d0
    cmp.b   write_buffer_drive,d0
    bne     write_disk4     ; select_driveと書き込み用バッファのデータを保持ているドライブ番号(write_buffer_drive)が不一致ならwrite_disk4にジャンプ
    move.b  track,d0
    cmp.b   write_buffer_track,d0
    beq     write_disk1     ; trackとwrite_buffer_trackが等しければ、すでにpre-read済み。つまりライト用バッファにはデータがある。wtite_disk1にジャンプ
write_disk4:
    tst.l   write_flag  ; write_bufferが更新されているか調べる。write_flag=1なら更新されている
    beq     write_disk5 ; 0ならpre-readを実行する
    bsr     wrtkw       ; 更新されているならdiskを読み、write_bufferへ書き込む
    bcc     write_disk5
    rts
write_disk5:
    bsr     pre_read    ; pre-readを実行
    bcc     write_disk1 ; no error then go wrired1 / emulator always success.
    rts
;   該当するセクタ位置におけるwrite_bufferの128バイトをCP/Mのdmaが指すアドレスにコピーする
write_disk1:
    clr.l   d0
    move.b  sector,d0
;   subq.b  #1,d0       ; セクタは0から始まるので、この処理は行わない。
    lsl.l   #7,d0
    lea     write_buffer,a0
    adda.l  d0,a0
    movea.l dma,a2
    moveq   #128,d0
write_disk2:
    move.b  (a2)+,(a0)+
    subq.b  #1,d0
    bne     write_disk2
    move.l  write_flag,d0
    ori.b   #1,d0
    move.l  d0,write_flag   ; write_flag = 1としてライト用バッファが更新されたことを示す
    clr.l   d0
    cmpi.b  #1,write_type   ; write_type=1ならディレクトリ書き込み。write_dirへジャンプする
    bne     write_disk3
    bsr     write_dir
write_disk3:
    move.b  select_drive,d0
    cmp.b   write_buffer_drive,d0
    beq     go_sync ; もしread_buffer's drive == write_buffer's driveなら、同期する必要があるかもしれない。go_syncへジャンプする
    clr.l   d0
    rts
go_sync:
    move.b  write_buffer_track,d0
    cmp.b   read_buffer_track,d0
    beq     sync_buffer     ; もしread_buffer's track == write_buffer's trackなら両方のバッファの内容を同期させる必要がある。sync_bufferへジャンプする
    clr.l   d0
    rts

;
; リード用バッファとライト用バッファの内容を同期させる
;

sync_buffer:
    clr.l   d0
    move.b  sector,d0
;*  subq.b  #1,d0
    lsl.l   #7,d0
    lea     write_buffer,a0
    lea     read_buffer,a1
    adda.l  d0,a1
    adda.l  d0,a0
    moveq   #32,d1
sync_buffer1:
    move.l  (a0)+,(a1)+
    subq.b  #1,d1
    bne     sync_buffer1
    clr.l   d0
    rts

wrtkw:
    move.b  write_buffer_track,d0
    cmp.b   #$ff,d0
    bne     wrtkw1      ; 唯一、wbootからコールされた時だけ、d0をクリアして返る?
    clr.l   d0
    rts
wrtkw1:
    clr.l   write_flag
    bsr     write_track
    clr.l   d0          ; emulator always success
    rts

write_track:
    move.l  #$03000001,d2   ; sector len:3(1024), side = 0, sector = 1
    move.b  write_buffer_track,d4

;   Side setting
    andi.l  #1,d4       ; d4 is side(0/1)
    lsl.l   #8,d4       ; left 8bit shift / side:bit 15-08

;   Track setting
    clr.l   d5
    move.b  write_buffer_track,d5
    lsr.b   #1,d5       ; d5 is physical track number

;   Drive number setting
    clr.l   d6
    move.b  read_buffer_drive,d6    ; d6 is drive number
    lsl.l   #8,d6

;*  DO WRITE
    clr.l   d0
    move.w  d5,d0       ; d5 is physical track number
    swap    d0          ; track:bit23-16
    add.l   d0,d2       ; set physical track no
    add.l   d4,d2       ; set side
    move.l  #$9000,d1   ; 2HD
    or.l    d6,d1       ; drive number
    ori.w   #$0070,d1   ; 0111_0000: MFM, RETRY, SEEK
    move.l  #$2000,d3   ; size 8 * 1024
    move.l  #write_buffer,a1    ; write_buffer to disk

    moveq.l #B_WRITE,d0
    trap    #IOCS
    rts


;
;   プリリードPre-read。指示トラック番号の内容をwrite_bufferに書き込む
;
pre_read:

    clr.l   write_flag

    move.l  #$03000001,d2   ; sector len:3(1024), side = 0, sector = 1

    move.b  track,d3
    move.b  d3,write_buffer_track   ; update write_buffer's track

;   Side setting
    move.b  write_buffer_track,d4
    andi.l  #1,d4       ; d4 is side(0/1)
    lsl.l   #8,d4       ; left 8bit shift / side:bit 15-08

;   Track setting
    clr.l   d5
    move.b  write_buffer_track,d5
    lsr.b   #1,d5       ; d5 is physical track number

;   Drive number setting
    clr.l   d6
    move.b  select_drive,d6 ; d6 is drive number
    move.b  d6,write_buffer_drive       ; update write_buffer's drive
    lsl.l   #8,d6

;   DO READ
    clr.l   d0
    move.w  d5,d0       ; d5 is physical track number
    swap    d0          ; track:bit23-16
    add.l   d0,d2       ; set physical track no
    add.l   d4,d2       ; set side
    move.l  #$9000,d1   ; 2HD
    or.l    d6,d1       ; drive number
    ori.w   #$0070,d1   ; 0111_0000: MFM, RETRY, SEEK
    move.l  #$2000,d3   ; size 8 * 1024
    move.l  #write_buffer,a1    ; to memory address

    moveq.l #B_READ,d0  ; READ track to write buffer
    trap    #IOCS

    andi    #0,CCR      ; clear carry flag -> mean success

    rts

;
; ディレクトリトラックへの書き込み
;
write_dir:
    clr.l   write_flag

    move.l  #$03000001,d2   ; sector len:3(1024), side = 0, sector = 1
    move.b  track,d4

;   Side setting
    andi.l  #1,d4       ; d4 is side(0/1)
    lsl.l   #8,d4       ; left 8bit shift / side:bit 15-08

;   Track setting
    clr.l   d5
    move.b  track,d5
    lsr.b   #1,d5       ; d5 is physical track number

;   Drive number setting
    clr.l   d6
    move.b  select_drive,d6 ; d6 is drive number
    lsl.l   #8,d6

* DO WRITE
    clr.l   d0
    move.w  d5,d0       ; d5 is physical track number
    swap    d0          ; track:bit23-16
    add.l   d0,d2       ; set physical track no
    add.l   d4,d2       ; set side
    move.l  #$9000,d1   ; 2HD
    or.l    d6,d1       ; drive number
    ori.w   #$0070,d1   ; 0111_0000: MFM, RETRY, SEEK
;   move.l  #$2000,d3   ; size 8 * 1024
    move.l  #$400,d3    ; size 1024 = 1 sector
    move.l  #write_buffer,a1        ;; to memory address

    moveq.l #B_WRITE,d0
    trap    #IOCS
    rts

;
; FLUSH
;
flush:
    tst.l   write_flag  ; write bufferは更新されたか? 1 is YES / 0 is NO
    bne     flush1  ; 更新されているのでflush1にジャンプ
    clr.l   d0      ; return successful
    rts
flush1:
    bsr     wrtkw
    rts

setexc:
;   andi.l  #$ff,d1     * do only for exceptions 0 - 255
;   lsl #2,d1       * multiply exception number by 4
;   movea.l d1,a0
;   move.l  (a0),d0     * return old vector value
;   move.l  d2,(a0)     * insert new vector
;   rts

    andi.l  #$ff,d1 ; do only for exceptions 0 - 255
    cmpi    #47,d1  ; 47d(2FH)
    beq     noset   ; this BIOS doesn't set Trap 15
    cmpi    #9,d1   ; or Trace
    beq     noset
    cmpi    #45,d1  ; this BIOS doesn't set Trap #13 / CTRL+C  
    beq     noset
    lsl     #2,d1   ; multiply exception number by 4
    movea.l d1,a0
    move.l  (a0),d0 ; return old vector value
    move.l  d2,(a0) ; insert new vector
noset:
    rts

;
;   section data
;

initmsg:
        dc.b   13,10,"CP/M-68K Version 1.2 for SHARP X68000, BIOS ver.1.0.6",13,10,0

    even

select_drive:       dc.b    $00 ; seldskの値(ドライブ番号)がセットされる。

track:              dc.b    $00 ; setrekの値(トラック番号)がセットされる。
read_buffer_track:  dc.b    $00 ; read_bufferはこのトラック番号の内容を保持ている。
write_buffer_track: dc.b    $00 ; write bufferはこのトラック番号の内容を保持している。

read_buffer_drive:  dc.b    $00
write_buffer_drive: dc.b    $00

    even

sector:     dc.w    0
dma:        dc.l    0
write_type: dc.w    0
write_flag: dc.l    0

memrgn: dc.w    1   ; 1 memory region
    dc.l    $20000  ; starts at 30000 hex ; TPAのスタートアドレス
;   dc.l    $17800  ; goes until 18000 hex
    dc.l    $1C0000 ; goes until 1c000 hex ; TPAのメモリ量

;
; disk parameter headers
;
dph0:
    dc.l    xlt0
    dc.w    0       ; dummy
    dc.w    0
    dc.w    0
    dc.l    dir_buffer  ; pointer to directory buffer
    dc.l    dpb     ; pointer to disk parameter block
    dc.l    ckv0    ; pointer to check vector
    dc.l    alv0    ; pointer to allocation vector

dph1:
    dc.l    xlt0
    dc.w    0       ; dummy
    dc.w    0
    dc.w    0
    dc.l    dir_buffer  ; pointer to directory buffer
    dc.l    dpb     ; pointer to disk parameter block
    dc.l    ckv1    ; pointer to check vector
    dc.l    alv1    ; pointer to allocation vector

;
; disk parameter block
;

dpb:
    dc.w    64  ; 1トラックあたりの論理セクタ数。両サイドの合計ではない。物理セクタ数でもない。
    dc.b    6   ; block shiftS
    dc.b    63  ; block mask
    dc.b    7   ; extent mask
    dc.b    0   ; dummy fill
    dc.w    149 ; disk size
    dc.w    127 ; 128-1 directory entries
    dc.w    $c000   ; directory mask
    dc.w    32  ; directory check size
    dc.w    4   ; track offset.CP/M-68Kのシステムサイズは約25KB。記録するには4トラック必要。

;
; sector translate table
;
;xlt:
;   dc.b    1,2,3,4,5,6,7,8
;   dc.b    9,10,11,12,13,14,15,16
;   dc.b    17,18,19,20,21,22,23,24
;   dc.b    25,26

;   section bss

read_buffer:    ds.b    $2000
write_buffer:   ds.b    $2000

dir_buffer: ds.b    128 ; directory buffer

ckv0:   ds.b    32  ; check vector
ckv1:   ds.b    32

alv0:   ds.b    64  ; allocation vector
alv1:   ds.b    64


;keyboard_check:    dc.l    $10

    end

 

LEAでアセンブルして、出来上がったバイナリをCPMのシステムに張り付けてCPM.SYSを作ってください。あとはブートローダーに書いたディスクの位置に書き込めば完了です。

 

 

 

 

 

 

 

FreeDOSでネットにつなぐ

FreeDOSでは、LANカードのパケットドライバがあればネットに接続することができます。メジャーなカードなら大抵パケットドライバがあるので、DOS環境で手軽にネット接続できるようになります。

 

FreeDOSのアプリ管理プログラムFDIMPLESからmTCPをインストールしておき、パケットドライバをC:\NET\FDNET\にコピーしておきます。

C:\NET\FDNET\FDNETPD.BATに、私の環境では以下のように書きました。

 

fetpkt -a 0x60

 

これで、リブートするとDHCPでIPを取ってくれます。

 

FreeDOSにはFTPクライアントがあるので、こちらでFTPサーバーを立てれば、ファイルのやり取りができます。

 

 

TEAC MT-2ST/N50をFreeDOSで動かす

SORD FUTURE32αにはバックアップ装置としてテープストリーマーが搭載されています。入手したときに興味を持ち、実際に動かしてみたいと思っていました。

 

今回取り上げたのはTEAC MT-2ST/N50です。このバックアップ装置は、磁気データカセット(D/CASとも言う)を使ったものです。磁気データカセットは、音楽カセットテープに形状が似た記録媒体です。

 

磁気データカセット

 

磁気データカセット

 

この装置をFreeDOS上で動かすことができたので、必要事項を忘れないように書いておきます。

 

TEAC MT-2ST/N50

TEAC MT-2ST/N50は、SCSI接続のテープストリーマーで150MBの記録容量を持ちます。専用のテープ(TEAC CT-600N)に記録します。これ以外のテープでは記録できません。しかし、CT-500H、CT-600Hでは読み込むことはできるそうです。

また、PC-9801用のテープバックアップ装置であるNEC PC-98B55 カセットマグネチックユニットの中身は、TEAC MT-2ST/N50です。なので、専用磁気データカセット( 

NEC DATA CASSETTE 180HD)は、TEAC CT-600Nと同等品です。実際に180HDでも記録できました。

 

FreeDOS

FreeDOSSCSIカードとしてadaptecのAHA-2940AUを挿して、MT2ST/N50を接続します。FreeDOSのTARではASPIを使うので、ASPIマネージャをfdconfig.sysに追加します。

 

プロンプトから以下のようにコマンドを入力します。

 

tar cvf aspi .

 

これで、現在のディレクトリのファイルがアーカイブされます。テープストリーマーを直接指定しなくても1台しかなければ、ASPIだけで動きました。

 

 

 

SHARP X68000にCP/M-68Kを移植する(その4:ディスクイメージ)

X68000CP/M-68K(ver1.2)のディスクイメージ(d88とXDF)をGoogle Driveに置きました。イメージの入ったディレクトリへのリンクを張っておきます。X68000Zでも動くようです(Cコンパイラのディスクだけ確認しました)。

 

DISK1~8は、CP/M-68K(Ver1.2)の配布ファイル群をX68k用のCP/Mシステムを入れたディスクにコピーしたものです。

 

cpm68k_c_disk.d88(xdf)は、Cコンパイラが使える様にファイルを集めたディスクです。プロンプトから、

 

C.SUB hoge

 

と入力すれば、コンパイルされ、無事終わったら、

 

CLINK.SUB hoge

 

とすることでバイナリ(*.68K)ファイルが生成されます。エディタとしてEDが入っていますが、PCで編集して、l3diskexのようなディスク操作ツールで書き込んで実行した方が速いです。

 

drive.google.com

SHARP X68000にCP/M-68Kを移植する(その3:BIOSコード)

BIOSの全アセンブラコード

X68kCP/M-68KのBIOS(IOCS版)を示します。

 

;*****************************************************************
;*      CP/M-68K BIOS                                           *
;*  Basic Input/Output Subsystem                                *
;*  For SHARP X68000 with floppy disk controller                *
;*****************************************************************

_ccp:   equ $150b8

IOCS        equ $0f
B_KEYINP    equ $0
B_PUTC      equ $20
B_READ      equ $46
B_WRITE     equ $45

nfuncs  equ $17

maxdsk  equ $2      ; このBIOSでは2台のフロッピーディスクをサポートしている
dphlen  equ $1a     ; 1A=26 / disk parameter headerの長さは26
xlt0 equ 0

code    equ 1
data    equ 2
bss     equ 3

    section code

    org $1B000

_init:
    move.l  #traphndl,$8c   ; set up trap #3 handler
    move.l  #initmsg,a6
    bsr     prtstr
    clr.l   d0      ; log on disk A, user 0
    clr.l   write_flag
    clr.b   track
    move.b  #$ff,read_buffer_track  ; read bufferを不活化
    move.b  #$ff,write_buffer_track ; write bufferを不活化
    rts

traphndl:
    move.l  #biosbase,a0
    cmpi    #nfuncs,d0
    bcc     trapng
    lsl     #2,d0
    move.l  0(a0,d0),a0
    jsr (a0)
trapng:
    rte

biosbase:
    dc.l  _init
    dc.l  wboot
    dc.l  constat
    dc.l  conin
    dc.l  conout
    dc.l  lstout
    dc.l  pun
    dc.l  rdr
    dc.l  home
    dc.l  seldsk
    dc.l  settrk
    dc.l  setsec
    dc.l  setdma
    dc.l  read
    dc.l  write
    dc.l  listst
    dc.l  sectran
    dc.l  setdma
    dc.l  getseg
    dc.l  getiob
    dc.l  setiob
    dc.l  flush
    dc.l  setexc

;   nfuncs=(*-biosbase)/4 ; BIOSの関数の数

wboot:
    bsr flush
    move.b #$ff,read_buffer_track
    move.b #$ff,write_buffer_track
    jmp _ccp

constat:
    moveq.l #$0,d0      ; シリアルコンソールではなく画面出力を使うのでいつでも0にしておく
    rts

conin:
    moveq.l #B_KEYINP,d0
    trap    #IOCS
    andi.l  #$000000ff,d0 ; IOCSの返す値からASCIIコードを抽出する
    rts

conout:
    moveq.l #B_PUTC,d0
    trap    #IOCS
    rts

prtstr:
    move.l a6,a1
    moveq.l #$21,d0
    trap #IOCS
    rts

lstout:
pun:
rdr:
    rts

listst:
    move.b  #$0,d0
    rts

;
; Disk Handlers for X68000(uPD72065) IOCS version
;

home:
    clr.b   track
    rts

getseg:
    move.l  #memrgn,d0  ; return address of mem region table
    rts

getiob:
    rts

setiob:
    rts

seldsk:
;   select disk given by register d1.b
    moveq   #0,d0
    cmp.b   #maxdsk,d1  ; 2台を超えてたら、そのままエラーにする
    bpl     selrtn      ; if no, return 0 in d0 / if plus then illegal drive no.

    move.b  d1,select_drive ; 正しいドライブ数なら、d1をselect_driveにセーブする
    move.b  select_drive,d0
    mulu    #dphlen,d0  ; dphlen(1Ah) * d0
    add.l   #dph0,d0    ; point d0 at correct dph
selrtn:
    rts

settrk:
    move.b  d1,track    ; update current track
    rts

setsec:
    move.b  d1,sector
    rts

sectran:
;   translate sector in d1 with translate table pointed to by d2
;   result in d0
    move.l  d1,d0   ; 今回はセクタ変換(セクタスキュー)をしないので、d1をそのままd0に入れる
    rts

setdma:
    move.l  d1,dma ; CP/MのDMAはシステムが読み書きするアドレスを指している。いわゆるDMAとはもはや無関係
    rts

;
; READルーチン
;

read:
; Read one sector from requested disk, track, sector to dma address
; Retry if necessary, return in d0 00 if ok, else non-zero
    bsr     read_disk
    move.l  #$0,d0 ; エミュレータ、X68000Zでは常にこの処理は成功する
    rts

;
; read 128 byte from read_buffer
;

read_disk:
    move.b  select_drive,d0
    cmp.b   read_buffer_drive,d0
    bne     read_disk3      ; select drive != current drive ならDISKを読み、read_bufferをCP/MのDMAの指すアドレスにコピーする
    move.b  track,d0
    cmp.b   read_buffer_track,d0
    beq     read_disk1      ; track == read_buffer’track ならread_bufferを読みCP/MのDMAの指すアドレスにコピーする
read_disk3:
    bsr     read_track
;   bcc     $+4         ; エミュレータ、X68000Zは常に、この処理は成功する
;   rts
read_disk1:                 ; read_bufferからデータを読み、CP/MのDMAの指すアドレスにコピーする
    clr.l   d0
    move.b  sector,d0
    addq.b  #1,d0       ; CP/Mはなぜか理論セクタを0から始める。足して引くという意味のないことをしている
    subq.b  #1,d0       ; そのうち消す予定
    lsl.l   #7,d0       ; 128 * sector ; 該当するsectorがread_bufferのどこにあるか計算する
    lea     read_buffer,a0
    adda.l  d0,a0
    movea.l dma,a2
    moveq   #128,d0
read_disk2:             ; 該当するsectorが記録されているread_bufferの128バイトをCP/MのDMAの指すアドレスにコピーする
    move.b  (a0)+,(a2)+
    subq.b  #1,d0
    bne     read_disk2
    clr.l   d0
    rts
read_track:
    move.l  #$03000001,d2   ; sector len:3(1024), side = 0, sector = 1
    move.b  track,d4
    move.b  d4,read_buffer_track    ; read_bufferがどのトラックを記録しているのかを更新する

;   Side setting
    andi.l  #1,d4       ; d4 is side(0/1)
    lsl.l   #8,d4       ; left 8bit shift / side:bit 15-08

;   Track setting
    clr.l   d5
    move.b  track,d5
    lsr.b   #1,d5       ; d5は物理トラック番号

;   Drive numver setting
    clr.l   d6
    move.b  select_drive,d6 ; d6はドライブ番号
    move.b  d6,read_buffer_drive ; read_bufferがドライブ番号(のトラック番号)を記録しているのかを更新する
    lsl.l   #8,d6

;   DO READ
    clr.l   d0
    move.w  d5,d0       ; d5 is physical track number
    swap    d0          ; track:bit23-16
    add.l   d0,d2       ; set physical track no.
    add.l   d4,d2       ; set side no.
    move.l  #$9000,d1   ; 2HD
    or.l    d6,d1       ; drive number
    ori.w   #$0070,d1   ; 0111_0000: MFM, RETRY, SEEK
    move.l  #$2000,d3   ; size 8 * 1024
    move.l  #read_buffer,a1 ; to memory address

    moveq.l #B_READ,d0
    trap    #IOCS
    rts

;
; WRITEルーチン
;

write:
    bsr     write_disk
    move.l  #0,d0 ; emulator says always success
    rts

;
;   write 128 bytes to disk
;

write_disk:
    move.b  d1,write_type   ; d1.bをwite_typeにセーブする。これはdirectory書き込みの時は1になる。通常書き込みの時は0になる
    move.b  select_drive,d0
    cmp.b   write_buffer_drive,d0
    bne     write_disk4     ; select_driveと書き込み用バッファのデータを保持ているドライブ番号(write_buffer_drive)が不一致ならwrite_disk4にジャンプ
    move.b  track,d0
    cmp.b   write_buffer_track,d0
    beq     write_disk1     ; trackとwrite_buffer_trackが等しければ、すでにpre-read済み。つまりライト用バッファにはデータがある。wtite_disk1にジャンプ
write_disk4:
    tst.l   write_flag  ; write_bufferが更新されているか調べる。write_flag=1なら更新されている
    beq     write_disk5 ; 0ならpre-readを実行する
    bsr     wrtkw       ; 更新されているならdiskを読み、write_bufferへ書き込む
    bcc     write_disk5
    rts
write_disk5:
    bsr     pre_read    ; pre-readを実行
    bcc     write_disk1 ; no error then go wrired1 / emulator always success.
    rts
;   該当するセクタ位置におけるwrite_bufferの128バイトをCP/Mのdmaが指すアドレスにコピーする
write_disk1:
    clr.l   d0
    move.b  sector,d0
;   subq.b  #1,d0       ; セクタは0から始まるので、この処理は行わない。
    lsl.l   #7,d0
    lea     write_buffer,a0
    adda.l  d0,a0
    movea.l dma,a2
    moveq   #128,d0
write_disk2:
    move.b  (a2)+,(a0)+
    subq.b  #1,d0
    bne     write_disk2
    move.l  write_flag,d0
    ori.b   #1,d0
    move.l  d0,write_flag   ; write_flag = 1としてライト用バッファが更新されたことを示す
    clr.l   d0
    cmpi.b  #1,write_type   ; write_type=1ならディレクトリ書き込み。write_dirへジャンプする
    bne     write_disk3
    bsr     write_dir
write_disk3:
    move.b  select_drive,d0
    cmp.b   write_buffer_drive,d0
    beq     go_sync ; もしread_buffer's drive == write_buffer's driveなら、同期する必要があるかもしれない。go_syncへジャンプする
    clr.l   d0
    rts
go_sync:
    move.b  write_buffer_track,d0
    cmp.b   read_buffer_track,d0
    beq     sync_buffer     ; もしread_buffer's track == write_buffer's trackなら両方のバッファの内容を同期させる必要がある。sync_bufferへジャンプする
    clr.l   d0
    rts

;
; リード用バッファとライト用バッファの内容を同期させる
;

sync_buffer:
    clr.l   d0
    move.b  sector,d0
;*  subq.b  #1,d0
    lsl.l   #7,d0
    lea     write_buffer,a0
    lea     read_buffer,a1
    adda.l  d0,a1
    adda.l  d0,a0
    moveq   #32,d1
sync_buffer1:
    move.l  (a0)+,(a1)+
    subq.b  #1,d1
    bne     sync_buffer1
    clr.l   d0
    rts

wrtkw:
    move.b  write_buffer_track,d0
    cmp.b   #$ff,d0
    bne     wrtkw1      ; 唯一、init,wbootからコールされた時だけ、d0をクリアして返る?
    clr.l   d0
    rts
wrtkw1:
    clr.l   write_flag
    bsr     write_track
    clr.l   d0          ; emulator always success
    rts

write_track:
    move.l  #$03000001,d2   ; sector len:3(1024), side = 0, sector = 1
    move.b  write_buffer_track,d4

;   Side setting
    andi.l  #1,d4       ; d4 is side(0/1)
    lsl.l   #8,d4       ; left 8bit shift / side:bit 15-08

;   Track setting
    clr.l   d5
    move.b  write_buffer_track,d5
    lsr.b   #1,d5       ; d5 is physical track number

;   Drive number setting
    clr.l   d6
    move.b  read_buffer_drive,d6    ; d6 is drive number
    lsl.l   #8,d6

;*  DO WRITE
    clr.l   d0
    move.w  d5,d0       ; d5 is physical track number
    swap    d0          ; track:bit23-16
    add.l   d0,d2       ; set physical track no
    add.l   d4,d2       ; set side
    move.l  #$9000,d1   ; 2HD
    or.l    d6,d1       ; drive number
    ori.w   #$0070,d1   ; 0111_0000: MFM, RETRY, SEEK
    move.l  #$2000,d3   ; size 8 * 1024
    move.l  #write_buffer,a1    ; write_buffer to disk

    moveq.l #B_WRITE,d0
    trap    #IOCS
    rts


;
;   プリリードPre-read。指示トラック番号の内容をwrite_bufferに書き込む
;
pre_read:

    clr.l   write_flag

    move.l  #$03000001,d2   ; sector len:3(1024), side = 0, sector = 1

    move.b  track,d3
    move.b  d3,write_buffer_track   ; update write_buffer's track

;   Side setting
    move.b  write_buffer_track,d4
    andi.l  #1,d4       ; d4 is side(0/1)
    lsl.l   #8,d4       ; left 8bit shift / side:bit 15-08

;   Track setting
    clr.l   d5
    move.b  write_buffer_track,d5
    lsr.b   #1,d5       ; d5 is physical track number

;   Drive number setting
    clr.l   d6
    move.b  select_drive,d6 ; d6 is drive number
    move.b  d6,write_buffer_drive       ; update write_buffer's drive
    lsl.l   #8,d6

;   DO READ
    clr.l   d0
    move.w  d5,d0       ; d5 is physical track number
    swap    d0          ; track:bit23-16
    add.l   d0,d2       ; set physical track no
    add.l   d4,d2       ; set side
    move.l  #$9000,d1   ; 2HD
    or.l    d6,d1       ; drive number
    ori.w   #$0070,d1   ; 0111_0000: MFM, RETRY, SEEK
    move.l  #$2000,d3   ; size 8 * 1024
    move.l  #write_buffer,a1    ; to memory address

    moveq.l #B_READ,d0  ; READ track to write buffer
    trap    #IOCS

    andi    #0,CCR      ; clear carry flag -> mean success

    rts

;
; ディレクトリトラックへの書き込み
;
write_dir:
    clr.l   write_flag

    move.l  #$03000001,d2   ; sector len:3(1024), side = 0, sector = 1
    move.b  track,d4

;   Side setting
    andi.l  #1,d4       ; d4 is side(0/1)
    lsl.l   #8,d4       ; left 8bit shift / side:bit 15-08

;   Track setting
    clr.l   d5
    move.b  track,d5
    lsr.b   #1,d5       ; d5 is physical track number

;   Drive number setting
    clr.l   d6
    move.b  select_drive,d6 ; d6 is drive number
    lsl.l   #8,d6

* DO WRITE
    clr.l   d0
    move.w  d5,d0       ; d5 is physical track number
    swap    d0          ; track:bit23-16
    add.l   d0,d2       ; set physical track no
    add.l   d4,d2       ; set side
    move.l  #$9000,d1   ; 2HD
    or.l    d6,d1       ; drive number
    ori.w   #$0070,d1   ; 0111_0000: MFM, RETRY, SEEK
;   move.l  #$2000,d3   ; size 8 * 1024
    move.l  #$400,d3    ; size 1024 = 1 sector
    move.l  #write_buffer,a1        ;; to memory address

    moveq.l #B_WRITE,d0
    trap    #IOCS
    rts

;
; FLUSH
;
flush:
    tst.l   write_flag  ; write bufferは更新されたか? 1 is YES / 0 is NO
    bne     flush1  ; 更新されているのでflush1にジャンプ
    clr.l   d0      ; return successful
    rts
flush1:
    bsr     wrtkw
    rts

setexc:
    andi.l  #$ff,d1 ; do only for exceptions 0 - 255
    cmpi    #47,d1
    beq     noset   ; this BIOS doesn't set Trap 15
    cmpi    #9,d1   ; or Trace
    beq     noset
    lsl     #2,d1   ; multiply exception nmbr by 4
    movea.l d1,a0
    move.l  (a0),d0 ; return old vector value
    move.l  d2,(a0) ; insert new vector
noset:  rts

;
;   section data
;

initmsg:
        dc.b   13,10,"CP/M-68K Version 1.2 for SHARP X68000",13,10,0

    even

select_drive:       dc.b    $00 ; seldskの値(ドライブ番号)がセットされる。

track:              dc.b    $00 ; setrekの値(トラック番号)がセットされる。
read_buffer_track:  dc.b    $00 ; read_bufferはこのトラック番号の内容を保持ている。
write_buffer_track: dc.b    $00 ; write bufferはこのトラック番号の内容を保持している。

read_buffer_drive:  dc.b    $00
write_buffer_drive: dc.b    $00

    even

sector:     dc.w    0
dma:        dc.l    0
write_type: dc.w    0
write_flag: dc.l    0

memrgn: dc.w    1   ; 1 memory region
    dc.l    $20000  ; starts at 30000 hex ; TPAのスタートアドレス
;   dc.l    $17800  ; goes until 18000 hex
    dc.l    $1C0000 ; goes until 1c000 hex ; TPAのメモリ量

;
; disk parameter headers
;
dph0:
    dc.l    xlt0
    dc.w    0       ; dummy
    dc.w    0
    dc.w    0
    dc.l    dir_buffer  ; pointer to directory buffer
    dc.l    dpb     ; pointer to disk parameter block
    dc.l    ckv0    ; pointer to check vector
    dc.l    alv0    ; pointer to allocation vector

dph1:
    dc.l    xlt0
    dc.w    0       ; dummy
    dc.w    0
    dc.w    0
    dc.l    dir_buffer  ; pointer to directory buffer
    dc.l    dpb     ; pointer to disk parameter block
    dc.l    ckv1    ; pointer to check vector
    dc.l    alv1    ; pointer to allocation vector

;
; disk parameter block
;

dpb:
    dc.w    64  ; 1トラックあたりの論理セクタ数。両サイドの合計ではない。物理セクタ数でもない。
    dc.b    6   ; block shiftS
    dc.b    63  ; block mask
    dc.b    7   ; extent mask
    dc.b    0   ; dummy fill
    dc.w    149 ; disk size
    dc.w    127 ; 128-1 directory entries
    dc.w    $c000   ; directory mask
    dc.w    32  ; directory check size
    dc.w    4   ; track offset.CP/M-68Kのシステムサイズは約25KB。記録するには4トラック必要。

;
; sector translate table
;
;xlt:
;   dc.b    1,2,3,4,5,6,7,8
;   dc.b    9,10,11,12,13,14,15,16
;   dc.b    17,18,19,20,21,22,23,24
;   dc.b    25,26

;   section bss

read_buffer:    ds.b    $2000
write_buffer:   ds.b    $2000

dir_buffer: ds.b    128 ; directory buffer

ckv0:   ds.b    32  ; check vector
ckv1:   ds.b    32

alv0:   ds.b    64  ; allocation vector
alv1:   ds.b    64

    end