tunozemichanの日記 / tunozemichan's diary

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

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を作ってください。あとはブートローダーに書いたディスクの位置に書き込めば完了です。