misc/stupsnd.s
;========================================================================================
; stupsnd.s
; Copyright (C) 2003-2025 Makoto Kamada
;
; This file is part of the XEiJ (X68000 Emulator in Java).
; You can use, modify and redistribute the XEiJ if the conditions are met.
; Read the XEiJ License for more details.
; https://stdkmd.net/xeij/
;========================================================================================
;----------------------------------------------------------------------------------------
; stupsnd.x
; X68030以上とIPLROM 1.6の起動音をon/offする
; オプション
; off
; 起動音を鳴らさない
; on
; 起動音をキーコード76(o5c)で鳴らす
; 2..127
; $02..$7F
; o5cなど
; 起動音を指定されたキーコードで鳴らす
; play
; 今すぐ起動音を再生する
; FM音源ドライバを外しておくこと
; 音色パラメータ
; kc,kf,tm,va[0],...,va[54]
; 音色パラメータを指定する
; kc キーコード
; kf キーフラクション
; tm キーオンからキーオフまでの時間(単位は1万分の1秒)
; va OPMDRV.Xと共通の音色パラメータ
; save
; レジスタデータをstupsnd.datに保存する
; 起動音に反映させるには、データをIPLROM 1.6へ埋め込む必要がある
;----------------------------------------------------------------------------------------
TITLE reg 'stupsnd.x (2025-04-13)'
.include control2.mac
.include doscall.mac
.include fefunc.mac
.include iocscall.mac
.include mfp.equ
.include misc.mac
.include push2.mac
.include sram.equ
.include sysport.equ
;グローバルレジスタを決める
dPREV reg d6 ;変更前の設定。0=off,1..255=on,2..127=kc
dMODE reg d7 ;変更後の設定。-1=なし,0=off,1..255=on,2..127=kc
aBASE reg a6 ;相対アクセスベースアドレス
base:
lea.l base(pc),aBASE
r reg -base(aBASE)
;引数を読み取る
addq.l #1,a2 ;コマンドライン
moveq.l #-1,dMODE ;変更後の設定なし
clr.b (param_array)r ;音色パラメータなし
sf.b (play_flag)r ;今すぐ起動音を再生しない
sf.b (save_flag)r ;レジスタデータを保存しない
dostart
lea.l buffer(pc),a0
movestr <'play'>,a1
bsr stricmp
if eq
st.b (play_flag)r ;今すぐ起動音を再生する
restart
endif
movestr <'save'>,a1
bsr stricmp
if eq
st.b (save_flag)r ;レジスタデータを保存する
restart
endif
moveq.l #$20,d0
or.b (a0),d0
if <cmp.b #'o',d0>,eq ;off,on,o5cなど
addq.l #1,a0
moveq.l #$20,d0
or.b (a0),d0
if <cmp.b #'f',d0>,eq
addq.l #1,a0
moveq.l #$20,d0
or.b (a0),d0
if <cmp.b #'f',d0>,eq
addq.l #1,a0
moveq.l #0,d0 ;off
else
goto help
endif
elif <cmp.b #'n',d0>,eq
addq.l #1,a0
moveq.l #1,d0 ;on
else
moveq.l #-'0',d0
add.b (a0)+,d0
goto <cmp.b #'8'-'0',d0>,hi,help
moveq.l #0,d1
move.b d0,d1
mulu.w #12,d1 ;番号
moveq.l #$20,d0
or.b (a0)+,d0
sub.b #'a',d0
goto <cmp.b #'g'-'a',d0>,hi,help
lea.l note_to_number(pc),a1
add.b (a1,d0.w),d1
ifor <cmpi.b #'#',(a0)>,eq,<cmpi.b #'+',(a0)>,eq ;シャープ
addq.l #1,a0
addq.b #1,d1
elif <cmpi.b #'-',(a0)>,eq ;フラット
addq.l #1,a0
subq.b #1,d1
endif
goto <tst.b d1>,mi,help
move.l d1,d0 ;x1
divu.w #3,d1 ;x1/3
add.b d1,d0 ;x4/3。キーコード
gotoor <cmp.b #2,d0>,lo,<tst.b d0>,mi,help
endif
elif <cmp.b #'$',(a0)>,eq ;$02..$7F
addq.l #1,a0
bsr stoh
goto cs,help
moveq.l #2,d1
goto <cmp.l d1,d0>,lo,help
moveq.l #127,d1
goto <cmp.l d1,d0>,hi,help
elifand <cmp.b #'0',(a0)>,hs,<cmp.b #'9',(a0)>,ls ;2..127
bsr stou
goto cs,help
moveq.l #2,d1
goto <cmp.l d1,d0>,lo,help
moveq.l #127,d1
goto <cmp.l d1,d0>,hi,help
else
goto help
endif
if <cmpi.b #',',(a0)>,ne ;','がない。変更後の設定
move.l d0,dMODE
else ;','がある。音色パラメータ
gotoor <cmp.b #2,d0>,lo,<tst.b d0>,mi,help ;kc以外は不可
lea.l param_array(pc),a1
move.b d0,(a1)+ ;0:kc
moveq.l #57-1,d2
for d2
goto <cmpi.b #',',(a0)>,ne,help
addq.l #1,a0
if <cmp.b #'$',(a0)>,eq
addq.l #1,a0
bsr stoh
elifand <cmp.b #'0',(a0)>,hs,<cmp.b #'9',(a0)>,ls
bsr stou
else
goto help
endif
goto cs,help
if <cmp.w #56-1,d2>,hi ;1:kf
moveq.l #63,d1
goto <cmp.l d1,d0>,hi,help
move.b d0,(a1)+
elif eq ;2..3:tm
goto <cmp.l #65535,d0>,hi,help
move.w d0,(a1)+ ;tmは2バイト。偶数アドレスに配置すること
else ;4..58:va[0],...,va[54]
goto <cmp.l #255,d0>,hi,help
move.b d0,(a1)+
endif
next
endif
goto <tst.b (a0)>,ne,help
start
lea.l buffer(pc),a0
movea.l a2,a1
bsr argcpy
movea.l a1,a2
while ne ;引数がある
;音色パラメータをレジスタデータに変換する
lea.l param_array(pc),a0
move.b (a0)+,d0 ;kc
if ne
if <tst.l dMODE>,mi ;音色パラメータがあり変更後の設定がないとき
moveq.l #0,dMODE
move.b d0,dMODE ;音色パラメータのkcを変更後の設定にする
endif
move.b (a0)+,d1 ;kf
move.w (a0)+,d2 ;tmは2バイト。偶数アドレスに配置すること
bsr stupsnd_conv
endif
;スーパーバイザモードへ移行する
supervisormode
;変更前の設定を保存する
moveq.l #0,dPREV
move.b SRAM_STARTUP_SOUND,dPREV ;変更前の設定
;<dPREV.l:変更前の設定。0=off,1..255=on,2..127=kc
;設定を変更する
ifand <tst.l dMODE>,pl,<cmp.b dPREV,dMODE>,ne ;変更後の設定があり、変更前と変更後の設定が異なる
unlocksram ;SRAM書き込み許可
move.b dMODE,SRAM_STARTUP_SOUND ;変更後の設定
locksram ;SRAM書き込み禁止
endif
;今すぐ起動音を再生する
if <tst.b (play_flag)r>,ne
bsr play_stupsnd
endif
;設定を表示する
lea.l buffer(pc),a0
movestr <'startup sound switch is '>,a1
bsr strcpy
move.l dMODE,d0
if mi
move.l dPREV,d0
endif
if eq ;0=off
move.b #'o',(a0)+
move.b #'f',(a0)+
move.b #'f',(a0)+
else ;1..255=on
move.b #'o',(a0)+
move.b #'n',(a0)+
if <cmp.b #2,d0>,ge ;2..127=kc。キーコードが指定されている
movestr <' with key code '>,a1
bsr strcpy
bsr utos
move.b #' ',(a0)+
move.b #'(',(a0)+
move.b d0,d1 ;x1
lsr.b #2,d1 ;x1/4
sub.b d1,d0 ;x3/4。番号
divu.w #12,d0 ;ノート|オクターブ
move.l d0,d1
swap.w d1 ;ノート
if <cmp.w #9,d1>,hs ;c c# dは
addq.w #1,d0 ;1つ上のオクターブ
endif
move.b #'o',(a0)+
add.b #'0',d0
move.b d0,(a0)+
add.w d1,d1
lea.l number_to_note(pc),a1
adda.w d1,a1
move.b (a1)+,(a0)+
move.b (a1),d0
if ne
move.b d0,(a0)+
endif
move.b #')',(a0)+
endif
endif
bsr crlf
lea.l buffer(pc),a0
bsr print
;IPLROMのバージョンを確認する
IOCS _ROMVER
move.l #$13000000,d1 ;IPLROM 1.3以上
if <cmp.b #2,dMODE>,ge ;2..127=kc。キーコードが指定されている
move.l #$16240226,d1 ;IPLROM 1.6 (24-02-26)以上
endif
if <cmp.l d1,d0>,lo ;IPLROMのバージョンが合っていない
ifand <tst.b dMODE>,pl,<cmp.b dPREV,dMODE>,ne ;変更後の設定があり、変更前と変更後の設定が異なる
unlocksram ;SRAM書き込み許可
move.b dPREV,SRAM_STARTUP_SOUND ;変更前の設定を復元する
locksram ;SRAM書き込み禁止
endif
movestr <'unsupported IPLROM version',13,10>,a0
bsr print
endif
;ユーザモードへ復帰する
usermode
;レジスタデータを保存する
if <tst.b (save_flag)r>,ne
move.w #$0020,-(sp)
pea.l data_name(pc)
DOS _CREATE
addq.l #6,sp
move.l d0,d1
if mi
movestr <'create error',13,10>,a0
bsr print
else
moveq.l #42,d2 ;42バイト
move.l d2,-(sp)
pea.l stupsnd_data(pc) ;レジスタデータ
move.w d1,-(sp)
DOS _WRITE
move.l d0,d3
DOS _CLOSE
lea.l 10(sp),sp
if <cmp.l d2,d3>,ne ;ディスクフル?
pea.l data_name(pc)
DOS _DELETE
addq.l #4,sp
movestr <'write error',13,10>,a0
bsr print
endif
endif
endif
;終了する
DOS _EXIT
;オプションを表示してエラー終了する
help:
lea.l options(pc),a0
bsr print
move.w #1,-(sp)
DOS _EXIT2
;オプション
options:
.dc.b TITLE,13,10
.dc.b ' turn on/off startup sound switch of X68030 or higher and IPLROM 1.6',13,10
.dc.b 'options',13,10
.dc.b ' off',13,10
.dc.b ' turn off startup sound switch',13,10
.dc.b ' on',13,10
.dc.b ' turn on startup sound switch with key code 76 (o5c)',13,10
.dc.b ' 2..127',13,10
.dc.b ' $02..$7F',13,10
.dc.b ' o5c etc.',13,10
.dc.b ' turn on startup sound switch with specified key code',13,10
.dc.b ' play',13,10
.dc.b ' play startup sound right now',13,10
.dc.b ' FM sound driver must be removed',13,10
.dc.b 'tone parameters',13,10
.dc.b ' kc,kf,tm,va[0],...,va[54]',13,10
.dc.b ' specify tone parameters',13,10
.dc.b ' kc -- key code',13,10
.dc.b ' kf -- key fraction',13,10
.dc.b ' tm -- time from key-on to key-off (unit is 1/10,000 of a second)',13,10
.dc.b ' va -- tone parameters common to OPMDRV.X',13,10
.dc.b ' save',13,10
.dc.b ' save register data to stupsnd.dat',13,10
.dc.b ' to reflect in the startup sound, inserting data into IPLROM 1.6 is required',13,10
.dc.b 0
;ノートの変換表
note_to_number:
.dc.b 6 ;a
.dc.b 8 ;b
.dc.b -3 ;c
.dc.b -1 ;d
.dc.b 1 ;e
.dc.b 2 ;f
.dc.b 4 ;g
number_to_note:
.dc.b 'd#' ;0
.dc.b 'e',0 ;1
.dc.b 'f',0 ;2
.dc.b 'f#' ;3
.dc.b 'g',0 ;4
.dc.b 'g#' ;5
.dc.b 'a',0 ;6
.dc.b 'a#' ;7
.dc.b 'b',0 ;8
.dc.b 'c',0 ;9
.dc.b 'c#' ;10
.dc.b 'd',0 ;11
data_name:
.dc.b 'stupsnd.dat',0
.bss
.even
param_array:
.ds.b 59 ;音色パラメータ。kc,kf,tmH,tmL,va[0],...,va[54]。偶数アドレスに配置すること
play_flag:
.ds.b 1 ;-1=今すぐ起動音を再生する
save_flag:
.ds.b 1 ;-1=音色パラメータを保存する
buffer:
.ds.b 1024 ;文字列バッファ
.text
.even
;----------------------------------------------------------------
;起動音
CH equ 0
KC equ 76
KF equ 5
TM equ 2500/100
FLCON equ (1<<3)|3 ;|FL###|CON###|
SLOT equ %1101 ;|C2|M2|C1|M1|
WAVE equ 0
SYNC equ 1
SPEED equ 0
PMD equ 0
AMD equ 0
PMS equ 0
AMS equ 0
PAN equ %11 ;|R|L|
M1AR equ 6
M1D1R equ 11
M1D2R equ 4
M1RR equ 4
M1D1L equ 0
M1TL equ 29
M1KS equ 0
M1MUL equ 0
M1DT1 equ 0
M1DT2 equ 3
M1AMSEN equ 1
C1AR equ 31
C1D1R equ 6
C1D2R equ 0
C1RR equ 2
C1D1L equ 2
C1TL equ 40
C1KS equ 2
C1MUL equ 1
C1DT1 equ 0
C1DT2 equ 3
C1AMSEN equ 1
M2AR equ 3
M2D1R equ 2
M2D2R equ 7
M2RR equ 2
M2D1L equ 3
M2TL equ 28
M2KS equ 1
M2MUL equ 3
M2DT1 equ 0
M2DT2 equ 1
M2AMSEN equ 1
C2AR equ 31
C2D1R equ 21
C2D2R equ 6
C2RR equ 4
C2D1L equ 2
C2TL equ 2
C2KS equ 1
C2MUL equ 1
C2DT1 equ 0
C2DT2 equ 0
C2AMSEN equ 1
play_stupsnd:
push d0-d4/a0
move.b SRAM_STARTUP_SOUND,d3 ;0=off,1..255=on,2..127=kc
if ne ;on
moveq.l #$08,d1 ;KeyOff
moveq.l #(0<<3)|CH,d2 ;|-|C2|M2|C1|M1|CH###|
bsr stupsnd_opmset
lea.l stupsnd_data(pc),a0 ;レジスタデータの配列
moveq.l #$20+CH,d1 ;レジスタアドレス
moveq.l #28-1,d4 ;0..27
for d4
move.b (a0)+,d2 ;レジスタデータ
ifand <cmp.b #$28+CH,d1>,eq,<cmp.b #2,d3>,ge ;2..127=kc。キーコードが指定されている
move.b d3,d2
endif
bsr stupsnd_opmset
addq.b #8,d1 ;次のレジスタアドレス
next
moveq.l #6-1,d4 ;28..39
for d4
move.b (a0)+,d1 ;レジスタアドレス
move.b (a0)+,d2 ;レジスタデータ
bsr stupsnd_opmset
next
moveq.l #$08,d1 ;KeyOn
moveq.l #(SLOT<<3)|CH,d2 ;|-|C2|M2|C1|M1|CH###|
bsr stupsnd_opmset
moveq.l #0,d0
move.w (a0)+,d0 ;40..41。tm@100us
add.l d0,d0 ;tm@50us
jsr wait_50us ;50us単位のウェイト
; moveq.l #$08,d1 ;KeyOff
moveq.l #(0<<3)|CH,d2 ;|-|C2|M2|C1|M1|CH###|
bsr stupsnd_opmset
endif
pop
rts
stupsnd_opmset:
IOCS _OPMSET
rts
;レジスタデータ(42バイト)。偶数アドレスに配置すること
.even
stupsnd_data:
.ifdef STUPSNDDAT
.insert stupsnd.dat
.else
.dc.b (PAN<<6)|FLCON ;0 $20+CH |PAN##|FL###|CON###|
.dc.b KC ;1 $28+CH |-|KC#######|
.dc.b KF<<2 ;2 $30+CH |KF######|--|
.dc.b (PMS<<4)|AMS ;3 $38+CH |-|PMS###|--|AMS##|
.dc.b (M1DT1<<4)|M1MUL ;4 $40+CH M1 |-|DT1###|MUL####|
.dc.b (M2DT1<<4)|M2MUL ;5 $48+CH M2
.dc.b (C1DT1<<4)|C1MUL ;6 $50+CH C1
.dc.b (C2DT1<<4)|C2MUL ;7 $58+CH C2
.dc.b M1TL ;8 $60+CH M1 |-|TL#######|
.dc.b M2TL ;9 $68+CH M2
.dc.b C1TL ;10 $70+CH C1
.dc.b C2TL ;11 $78+CH C2
.dc.b (M1KS<<6)|M1AR ;12 $80+CH M1 |KS##|-|AR#####|
.dc.b (M2KS<<6)|M2AR ;13 $88+CH M2
.dc.b (C1KS<<6)|C1AR ;14 $90+CH C1
.dc.b (C2KS<<6)|C2AR ;15 $98+CH C2
.dc.b (M1AMSEN<<7)|M1D1R ;16 $A0+CH M1 |AMSEN|--|D1R#####|
.dc.b (M2AMSEN<<7)|M2D1R ;17 $A8+CH M2
.dc.b (C1AMSEN<<7)|C1D1R ;18 $B0+CH C1
.dc.b (C2AMSEN<<7)|C2D1R ;19 $B8+CH C2
.dc.b (M1DT2<<6)|M1D2R ;20 $C0+CH M1 |DT2##|-|D2R#####|
.dc.b (M2DT2<<6)|M2D2R ;21 $C8+CH M2
.dc.b (C1DT2<<6)|C1D2R ;22 $D0+CH C1
.dc.b (C2DT2<<6)|C2D2R ;23 $D8+CH C2
.dc.b (M1D1L<<4)|M1RR ;24 $E0+CH M1 |D1L####|RR####|
.dc.b (M2D1L<<4)|M2RR ;25 $E8+CH M2
.dc.b (C1D1L<<4)|C1RR ;26 $F0+CH C1
.dc.b (C2D1L<<4)|C2RR ;27 $F8+CH C2
.dc.b $18,SPEED ;28,29 $18 |LFRQ########|
.dc.b $19,(0<<7)|AMD ;30,31 $19 |0|AMD#######|
.dc.b $19,(1<<7)|PMD ;32,33 $19 |1|PMD#######|
.dc.b $1B,WAVE ;34,35 $1B |CT1|CT2|----|WAVE##|
.dc.b $01,SYNC<<1 ;36,37 $01 |------|LFORESET|-|
.dc.b $01,0<<1 ;38,39 $01 |------|LFORESET|-|
.dc.w TM ;40,41 tm@100us
;42
.even
.endif
;----------------------------------------------------------------
;音色パラメータをレジスタデータに変換する
;<d0.b:kc
;<d1.b:kf
;<d2.w:tm
;<a0.l:音色パラメータ(55バイト)。va[0],...,va[54]
; +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10
; 0 FLCON SLOT WAVE SYNC SPEED PMD AMD PMS AMS PAN 0
; 11 M1AR M1D1R M1D2R M1RR M1D1L M1TL M1KS M1MUL M1DT1 M1DT2 M1AMSEN
; 22 C1AR C1D1R C1D2R C1RR C1D1L C1TL C1KS C1MUL C1DT1 C1DT2 C1AMSEN
; 33 M2AR M2D1R M2D2R M2RR M2D1L M2TL M2KS M2MUL M2DT1 M2DT2 M2AMSEN
; 44 C2AR C2D1R C2D2R C2RR C2D1L C2TL C2KS C2MUL C2DT1 C2DT2 C2AMSEN
; 55 kc kf
stupsnd_conv:
push d0-d4/a0-a2,58
movea.l sp,a1
moveq.l #55-1,d3
for d3
move.b (a0)+,(a1)+
next
move.b d0,(a1)+ ;kc
move.b d1,(a1)+ ;kf
movea.l sp,a0 ;音色パラメータ+kc+kf(57バイト)。va[0],...,va[54],kc,kf
tas.b 5(a0) ;|1|PMD#######|
lea.l stupsnd_data(pc),a1 ;レジスタデータ(42バイト)
lea.l 100f(pc),a2 ;音色パラメータ→レジスタデータ変換表
moveq.l #0,d1
moveq.l #28+6-1,d3
for d3
if <cmp.b #6-1,d3>,ls ;最後の6個
move.b (a2)+,(a1)+ ;レジスタアドレス
endif
moveq.l #0,d0 ;レジスタデータ
dostart
move.b (a0,d1.w),d1 ;音色パラメータ+kc+kfに含まれるレジスタデータの部品
and.b (a2)+,d1 ;マスク
move.b (a2)+,d4 ;シフトカウント
lsl.b d4,d1
or.b d1,d0 ;部品を並べる
start
move.b (a2)+,d1 ;音色パラメータ+kc+kfのインデックス
while pl
move.b d0,(a1)+ ;レジスタデータ
next
move.w d2,(a1)+ ;40,41 tm
pop
rts
;音色パラメータ→レジスタデータ変換表
100:
;音色パラメータ+kc+kfのインデックス,マスク,シフトカウント,…,-1
.dc.b 9,3,6,0,63,0,-1 ;0 $20+CH |PAN##|FL###|CON###|
.dc.b 55+0,127,0,-1 ;1 $28+CH |-|KC#######|
.dc.b 55+1,63,2,-1 ;2 $30+CH |KF######|--|
.dc.b 7,7,4,6,3,0,-1 ;3 $38+CH |-|PMS###|--|AMS##|
.dc.b 11+8,7,4,11+7,15,0,-1 ;4 $40+CH M1 |-|DT1###|MUL####|
.dc.b 33+8,7,4,33+7,15,0,-1 ;5 $48+CH M2
.dc.b 22+8,7,4,22+7,15,0,-1 ;6 $50+CH C1
.dc.b 44+8,7,4,44+7,15,0,-1 ;7 $58+CH C2
.dc.b 11+5,127,0,-1 ;8 $60+CH M1 |-|TL#######|
.dc.b 33+5,127,0,-1 ;9 $68+CH M2
.dc.b 22+5,127,0,-1 ;10 $70+CH C1
.dc.b 44+5,127,0,-1 ;11 $78+CH C2
.dc.b 11+6,3,6,11+0,31,0,-1 ;12 $80+CH M1 |KS##|-|AR#####|
.dc.b 33+6,3,6,33+0,31,0,-1 ;13 $88+CH M2
.dc.b 22+6,3,6,22+0,31,0,-1 ;14 $90+CH C1
.dc.b 44+6,3,6,44+0,31,0,-1 ;15 $98+CH C2
.dc.b 11+10,1,7,11+1,31,0,-1 ;16 $A0+CH M1 |AMSEN|--|D1R#####|
.dc.b 33+10,1,7,33+1,31,0,-1 ;17 $A8+CH M2
.dc.b 22+10,1,7,22+1,31,0,-1 ;18 $B0+CH C1
.dc.b 44+10,1,7,44+1,31,0,-1 ;19 $B8+CH C2
.dc.b 11+9,3,6,11+2,31,0,-1 ;20 $C0+CH M1 |DT2##|-|D2R#####|
.dc.b 33+9,3,6,33+2,31,0,-1 ;21 $C8+CH M2
.dc.b 22+9,3,6,22+2,31,0,-1 ;22 $D0+CH C1
.dc.b 44+9,3,6,44+2,31,0,-1 ;23 $D8+CH C2
.dc.b 11+4,15,4,11+3,15,0,-1 ;24 $E0+CH M1 |D1L####|RR####|
.dc.b 33+4,15,4,33+3,15,0,-1 ;25 $E8+CH M2
.dc.b 22+4,15,4,22+3,15,0,-1 ;26 $F0+CH C1
.dc.b 44+4,15,4,44+3,15,0,-1 ;27 $F8+CH C2
;レジスタアドレス,音色パラメータ+kc+kfのインデックス,マスク,シフトカウント,…,-1
.dc.b $18,4,255,0,-1 ;28,29 $18 |LFRQ########|
.dc.b $19,6,127,0,-1 ;30,31 $19 |0|AMD#######|
.dc.b $19,5,127,0,-1 ;32,33 $19 |1|PMD#######|
.dc.b $1B,2,3,0,-1 ;34,35 $1B |CT1|CT2|----|WAVE##|
.dc.b $01,3,1,1,-1 ;36,37 $01 |------|LFORESET|-|
.dc.b $01,3,0,1,-1 ;38,39 $01 |------|LFORESET|-|
.even
;----------------------------------------------------------------
;引数をコピーする
; 空白を読み飛ばしてから次の空白の手前までコピーする
; "~"または'~'で囲むと引数に空白を含めることができる
; ""または''と書くと長さが0の引数を与えることができる
;<a0.l:コピー先のバッファの先頭
;<a1.l:コピー元の文字列の先頭
;>d0.l:0=引数がない,1=引数がある
;>a0.l:コピー先の文字列の末尾の0の位置
;>a1.l:コピー元の引数の直後。なければコピー元の文字列の末尾の0の位置
;>eq=引数がない,ne=引数がある
argcpy::
exg.l a0,a1
bsr nextword ;空白を読み飛ばす
exg.l a0,a1
if eq ;引数がない
clr.b (a0)
moveq.l #0,d0
rts
endif
dostart
if <cmp.b #'"',d0>,eq ;"~"
dostart
move.b d0,(a0)+ ;書き込む
start
move.b (a1)+,d0 ;次の文字
break2 eq ;引数が終わった
while <cmp.b #'"',d0>,ne
elif <cmp.b #39,d0>,eq ;'~'
dostart
move.b d0,(a0)+ ;書き込む
start
move.b (a1)+,d0 ;次の文字
break2 eq ;引数が終わった
while <cmp.b #$39,d0>,ne
else
move.b d0,(a0)+ ;書き込む
endif
start
move.b (a1)+,d0 ;次の文字
break eq ;引数が終わった
breakand <cmp.b #9,d0>,hs,<cmp.b #13,d0>,ls ;\t\n\v\f\rならば終了
while <cmp.b #' ',d0>,ne ;空白でなければ繰り返す
subq.l #1,a1 ;進み過ぎた分戻る
clr.b (a0)
moveq.l #1,d0
rts
;----------------------------------------------------------------
;改行をコピーする
;<a0.l:コピー先
;>a0.l:コピー先の0の位置
crlf::
move.b #13,(a0)+
move.b #10,(a0)+
clr.b (a0)
rts
;----------------------------------------------------------------
;16進数の文字を整数に変換する
;<d0.b:文字
;>d0.l:0~16。-1=16進数の文字ではない
;>n:mi=16進数の文字ではない
fromxdigit::
sub.b #'0',d0
blo 1f ;x<0
sub.b #('9'+1)-'0',d0
blo 3f ;0<=x<=9
subq.b #'A'-('9'+1),d0
blo 1f ;9<x<A
subq.b #('F'+1)-'A',d0
blo 2f ;A<=x<=F
sub.b #'a'-('F'+1),d0
blo 1f ;F<x<A
subq.b #('f'+1)-'a',d0
blo 2f ;a<=x<=f
1: moveq.l #-1,d0 ;x<0||9<x<A||F<x<a||f<x
rts
2: addq.b #6,d0 ;A<=x<=F||a<=x<=f
3: add.b #10,d0 ;0<=x<=9
ext.w d0
ext.l d0
rts
;----------------------------------------------------------------
;空白を読み飛ばす
;<a0.l:文字列
;>d0.l:最初の空白以外の文字または0
;>a0.l:最初の空白以外の文字または0の位置
;>z:ne=空白以外の文字がある,eq=空白以外の文字がない
nextword::
moveq.l #0,d0
do
move.b (a0)+,d0 ;次の文字
break eq ;0ならば終了
redo <cmp.b #' ',d0>,eq ;' 'ならば繰り返す
whileand <cmp.b #9,d0>,hs,<cmp.b #13,d0>,ls ;\t\n\v\f\rならば繰り返す
subq.l #1,a0 ;進み過ぎた分戻る
tst.l d0
rts
;----------------------------------------------------------------
;文字列を表示する
;<a0.l:文字列
print::
push d0
bsr strlen
move.l d0,-(sp)
move.l a0,-(sp)
move.w #1,-(sp)
DOS _WRITE
lea.l (10,sp),sp
pop
rts
;----------------------------------------------------------------
;16進数の文字列を符号なし整数に変換する
;<a0.l:16進数の文字列。先頭の空白は認めない
;>d0.l:符号なし整数。(csのとき)下位32ビット
;>a0.l:16進数ではない文字または0の位置
;>c:cc=成功,cs=16進数の文字がないまたはオーバーフロー
;>v:(csのとき)vc=16進数の文字がない,vs=オーバーフロー
stoh::
push d1-d2/a1
moveq.l #0,d1 ;符号なし整数
moveq.l #0,d2 ;オーバーフロー判定用
movea.l a0,a1 ;開始位置
dostart
or.l d1,d2 ;16倍する直前の値のOR
lsl.l #4,d1 ;16倍して
or.b d0,d1 ;1桁加える
start
move.b (a0)+,d0 ;次の文字
bsr fromxdigit
while pl ;16進数の文字ならば繰り返す
subq.l #1,a0 ;進み過ぎた分戻る
add.l #$F0000000,d2
subx.w d2,d2 ;16倍する直前の値のORの上位4ビットが0でないすなわちオーバーフローのとき-1、さもなくば0
and.w #%00011,d2 ;オーバーフローのときvs,cs、さもなくばvc,cc
if <cmpa.l a1,a0>,eq ;進んでいない
or.w #%00001,d2 ;16進数の文字がないときcs
endif
move.l d1,d0 ;符号なし整数
move.w d2,ccr
popm
rts
;----------------------------------------------------------------
;10進数の文字列を符号なし整数に変換する
;<a0.l:10進数の文字列。先頭の空白は認めない
;>d0.l:(ccのとき)符号なし整数。(csのとき)0=10進数の文字がない,-1=オーバーフロー
;>a0.l:(ccのとき)10進数の文字列の次の位置。(csのとき)変化しない
;>z:(ccのとき)eq=符号なし整数が0
;>v:(csのとき)vc=10進数の文字がない,vs=オーバーフロー
;>c:cs=10進数の文字がないまたはオーバーフロー
stou::
push d1-d2/a1
moveq.l #0,d0 ;符号なし整数
moveq.l #0,d1 ;文字
movea.l a0,a1 ;開始位置
dostart
goto <cmp.l #$1999999A,d0>,hs,20f ;10倍したらオーバーフローする
move.l d0,d2 ;1倍
lsl.l #2,d0 ;4倍
add.l d2,d0 ;5倍
add.l d0,d0 ;10倍して
add.l d1,d0 ;1桁加える
goto cs,20f ;オーバーフローした
start
move.b (a0)+,d1 ;次の文字
sub.b #'0',d1 ;整数にする
whileand <>,hs,<cmp.b #10,d1>,lo ;10進数の文字ならば繰り返す
subq.l #1,a0 ;進み過ぎた分戻る
goto <cmpa.l a1,a0>,eq,30f ;進んでいない。10進数の文字がない
tst.l d0 ;ne/eq,vc,cc
10: pop
rts
;オーバーフロー
20:
.if 0
do
move.b (a0)+,d1 ;次の文字
sub.b #'0',d1 ;整数にする
whileand <>,hs,<cmp.b #10,d1>,lo ;10進数の文字を読み飛ばす
subq.l #1,a0 ;進み過ぎた分戻る
.else
movea.l a1,a0 ;開始位置に戻る
.endif
moveq.l #-1,d0 ;オーバーフロー
move.w #%00011,ccr ;ne,vs,cs
goto 10b
;10進数の文字がない
30:
; moveq.l #0,d0 ;10進数の文字がない
move.w #%00101,ccr ;eq,vc,cs
goto 10b
;----------------------------------------------------------------
;文字列をコピーする
;<a0.l:コピー先
;<a1.l:コピー元
;>a0.l:コピー先の0の位置
;>a1.l:コピー元の0の次の位置
strcpy::
do
move.b (a1)+,(a0)+
while ne ;0でなければ繰り返す
subq.l #1,a0 ;進み過ぎた分戻る
rts
;----------------------------------------------------------------
;小文字化しながら文字列比較
; '['<'A'となることに注意。SJISは正しく比較できないことに注意
;<a0.l:文字列0。引かれる
;<a1.l:文字列1。引く
;>ccr:eq=文字列0==文字列1,lo=文字列0<文字列1,hi=文字列1<文字列0
stricmp::
push d0-d1/a0-a1
do
move.b (a0)+,d0
if eq
cmp.b (a1)+,d0
break
endif
move.b (a1)+,d1
ifand <cmp.b #'A',d0>,hs,<cmp.b #'Z',d0>,ls
or.b #$20,d0
endif
ifand <cmp.b #'A',d1>,hs,<cmp.b #'Z',d1>,ls
or.b #$20,d1
endif
cmp.b d1,d0
while eq
pop
rts
;----------------------------------------------------------------
;文字列の長さを数える
;<a0.l:文字列
;>d0.l:長さ
strlen::
move.l a0,d0 ;d0=先頭
do
tst.b (a0)+
while ne ;0でなければ繰り返す
subq.l #1,a0 ;進み過ぎた分戻る。a0=末尾
exg.l d0,a0 ;d0=末尾,a0=先頭
sub.l a0,d0 ;d0=末尾-先頭=長さ
rts
;----------------------------------------------------------------
;32ビット符号なし整数を10進数の文字列に変換する
;<d0.l:符号なし整数
;<a0.l:バッファ。10進数の文字列の先頭
;>a0.l:10進数の文字列の末尾の0の位置
utos::
if <tst.l d0>,eq ;0
move.b #'0',(a0)+
else ;0以外
push d0-d2/a1
lea.l utos_table(pc),a1
do
move.l (a1)+,d1
while <cmp.l d1,d0>,lo ;引けるところまで進む。ゼロサプレス
do
moveq.l #'0'-1,d2
do
addq.b #1,d2
sub.l d1,d0
while hs ;引ける回数を数える
move.b d2,(a0)+
add.l d1,d0 ;引き過ぎた分を加え戻す
move.l (a1)+,d1
while ne
pop
endif
clr.b (a0)
rts
utos_table::
.dc.l 1000000000
.dc.l 100000000
.dc.l 10000000
.dc.l 1000000
.dc.l 100000
.dc.l 10000
.dc.l 1000
.dc.l 100
.dc.l 10
.dc.l 1
.dc.l 0
;----------------------------------------------------------------
;50us単位のウェイト
;<d0.l:待ち時間(50us単位)
wait_50us::
aTCDR reg a0
push d0-d2/aTCDR
lea.l MFP_TCDR,aTCDR ;Timer-Cデータレジスタ。200,199,…,2,1,0→200,199,…(50us単位)
moveq.l #0,d1
move.b (aTCDR),d1
move.b (aTCDR),d1 ;d1=前回の値
do
moveq.l #0,d2
move.b (aTCDR),d2 ;d2=今回の値
redo <cmp.b (aTCDR),d2>,lo ;0→200のとき読み直す
sub.w d2,d1 ;d1=前回の値-今回の値=経過時間
if lo ;0→200を跨いだとき
add.w #200,d1 ;今回の値が200増えた分経過時間が200減るので加え戻す
endif
exg.l d1,d2 ;d1=今回の値=次回の前回の値,d2=経過時間
sub.l d2,d0 ;d0=待ち時間-経過時間=次回の待ち時間
while hi ;待ち時間がなくなるまで繰り返す
pop
rts
.end