;
; MIDI Sequencer (Data Recorder/Player)
;
;  PIC16F84@10MHz
;
; 本ソースは秋月のPA.EXE拡張インストラクションを使用しております。
;   2001.2 (c) RUU
;
;
; 変更履歴
;       2001.2.18 (1.0) 最初のバージョン
;


            .include    16F84.h
            .osc        hs
            .wdt        off
            .pwrt       on
            .protect    off

trisa       equ     5
trisb       equ     6

MIDI_IN     equ     rb.0                        ; MIDIポート IN
MIDI_OUT    equ     ra.0                        ;            OUT

MSG_LED     equ     ra.1                        ; LED   Midi Message
ERR_LED     equ     ra.2                        ;       Error
REC_LED     equ     rb.4                        ;       Recording
PLAY_LED    equ     rb.5                        ;       Playing

SW_REC      equ     rb.1                        ; スイッチ  Record / Slow
SW_PLAY     equ     rb.2                        ;           Play / Fast
SW_STOP     equ     rb.3                        ;           Stop / Rec Start

SCL         equ     rb.6                        ; 24LC256 SCL
SDA         equ     rb.7                        ; 24LC256 SDA

SCLIO       equ     trisb.6                     ; I/O bit = trisb.6
SDAIO       equ     trisb.7                     ; I/O bit = trisb.7

CNT_BIT     equ     80                          ; 31.25Kbit/s タイマーWait値(bit間隔 32us)
CNT_STRT    equ     185                         ; StartBit検出～DataBit受信 TMR0値(dtカウント中の遅延分も考慮)
DT_UNIT     equ     20                          ; dt単位(*32us)
SPD_STEP    equ     1                           ; テンポの増減step
TEMPO_MIN   equ     10                          ; テンポ最小
TEMPO_MAX   equ     40                          ;       最大
DT_PREF1    equ     0f9h                        ; dtステータス(dt=1bytes):未定義のRealtimeMessageIDを使用
DT_PREF2    equ     0fdh                        ; dtステータス(dt=2bytes):〃
EOD         equ     0f4h                        ; End of Data:未定義のCommonMessageIDを使用
WAIT_CNT    equ     162                         ; 100usのwaitカウント

BUF_ADR     equ     0ch                         ; バッファアドレス
BUF_LEN     equ     32                          ; バッファ長

MEM_MAXH    equ     07fh                        ; 1チップのメモリサイズ(24LC256=32kb)
MEM_MAXL    equ     0ffh

            org     0ch

data_buf    ds      32                          ; MIDIデータの受信・メモリ書込み用FIFOバッファ
write_ptr   ds      1                           ; バッファ書込みptr
read_ptr    ds      1                           ; 〃　　　読出しptr

wsave       ds      1                           ; Wレジスタ退避
stsave      ds      1                           ; STATUS退避
fsrsave     ds      1                           ; fsr退避

send_buf    ds      1                           ; MIDIデータ送信用バッファ
midi_byt    ds      1                           ; MIDIデータバイト
bit_cnt     ds      1                           ; 受信ビットカウンタ
dt          ds      2                           ; デルタタイム(1カウント=DT_UNIT * 32us)
dt_cnt      ds      1                           ; dtサブカウンタ
play_dt     ds      1                           ; play時dtベース(テンポ;標準はDT_UNIT)

cnt         ds      1                           ; waitカウンタ
work        ds      1                           ; 
mem_io_dat  ds      1                           ; EEPROM R/W データ
rw_dat      ds      1                           ; EEPROM R/W ワーク
ACK_BIT     equ     rw_dat.0                    ; rw_dat.0 = acknowledge (0:ACK 1:NAK)

flags       ds      1                           ; Rec/Playモード、エラーフラグ
ERR_MO      equ     flags.0                     ; MemoryError       0:No Error  1:Memory Overflow
ERR_DO      equ     flags.1                     ; dtError           0:No Error  1:dt Overflow
MODE        equ     flags.7                     ; Mode              0:Record    1:Play
D_STAT      equ     flags.6                     ; PlayDataStatus    0:Empty     1:Data exist
IN_PAGE     equ     flags.5                     ; PageWriteStatus   0:ready     1:page writing

ctrl_byte   ds      1                           ; 24LC256 control code + chip select + r/w
R_W         equ     ctrl_byte.0

csb         ds      1                           ; ChipSelectBits(#0～#7)
mem_adr     ds      2                           ; memory Address(h/l)
mem_cpn     ds      1                           ; 実装メモリチップ数


            org     0
            goto    main
            org     4
            goto    intproc


;;;;;;;;;;;; 割り込み処理 intproc  ;;;;;;;;;;;;
intproc                                         ; 割り込み処理:MIDIデータ送受信/デルタタイムカウント/バッファ書込み
			mov		wsave,w						; Wレジスタ退避
			mov		stsave,status				; STATUS退避

            jb      MODE,int_play               ; if MODE == 1 then 再生(PLAY)へ

            ;; ----- 記録(RECORD)モード -----
int_rec
            cje     bit_cnt,#10,chk_strt        ; BYTE受信完了?
            cje     bit_cnt,#0,stop_bit         ; StopBit?

rec_byt     ;; MIDIデータバイト受信
            sub     tmr0,#CNT_BIT - 2           ; tmr0再設定(databit取得のため32us間隔で割り込み)
            clrb    t0if
            movb    c,MIDI_IN                   ; MIDIデータの1bit設定
            movb    MIDI_OUT,c                  ; MIDI THRU
            rr      midi_byt
            djnz    bit_cnt,int_end             ; Byte受信完了?
            call    write_buf                   ; then midiデータをWriteBufferに送る
            goto    int_end

chk_strt    ;; StartBitの検出,dtカウント
            jnb     intf,inc_dt                 ; INT割り込みでなければdtカウント

strt_bit    ;; StartBitを検出
            mov     tmr0,#CNT_STRT              ; tmr0に、StartBit確認～ビット7受信までのWait設定
            clrb    inte                        ; 以降 INT割り込みなし、TMR0割り込みあり
            clrb    intf
            setb    t0ie
            clrb    t0if
            clrb    MSG_LED                     ; MSG_LED点灯
            mov     bit_cnt,#8                  ; bit受信準備完了
            clrb    MIDI_OUT                    ; MIDI THRU

            ;; delta timeがあればWriteBufferに書き込む
            cje     dt[0],#0,check_dt1          ; dt が 1～FFh なら F9xxh (xx=dt[1])、
            mov     midi_byt,#DT_PREF2          ; dt が 100h～FFFF なら FDxxxxh (xxxx=dt[0][1])、
            call    write_buf                   ; を WriteBufferに転送し、さらにdtをクリアする
            mov     midi_byt,dt[0]
            clr     dt[0]
            call    write_buf
            goto    write_dt1
check_dt1
            cje     dt[1],#0,int_end
            mov     midi_byt,#DT_PREF1
            call    write_buf
write_dt1
            mov     midi_byt,dt[1]
            clr     dt[1]
            call    write_buf
            goto    int_end

inc_dt      ;; dtのカウント
            sub     tmr0,#CNT_BIT - 2           ; tmr0再設定(32us間隔)
            clrb    t0if
            djnz    dt_cnt,dt_end               ; 32*DT_UNIT(us)毎に
            mov     dt_cnt,#DT_UNIT
            incsz   dt[1]                       ; dt++
            goto    dt_end
            incsz   dt[0]
            goto    dt_end
            setb    ERR_DO                      ; dtオーバーフロー
dt_end
            jb      intf,strt_bit               ; dtカウント中にStartBit検出した
            goto    int_end

stop_bit    ;; StopBit
            sub     tmr0,#CNT_BIT - 2           ; tmr0再設定(32us間隔)
            clrb    t0if
            setb    MIDI_OUT                    ; MIDI THRU
            mov     dt_cnt,#DT_UNIT
            mov     bit_cnt,#10                 ; BYTE受信完了
            setb    MSG_LED                     ; MSG LED消灯
            setb    inte                        ; 以降 INT割り込み(StartBit検出)許可
            clrb    intf
            goto    int_end


            ;; ----- 再生(PLAY)モード -----
int_play
            sub     tmr0,#CNT_BIT - 2           ; tmr0再設定(32us間隔)
            clrb    t0if
            cje     bit_cnt,#10,dt_wait         ; dtウェイト中?
            cje     bit_cnt,#9,send_strt        ; 送信準備OK / StartBit ?
            cje     bit_cnt,#0,send_stop        ; StopBit

            ;; データ部(8bit)送信 1≦bit_cnt≦8
            rr      send_buf
            movb    MIDI_OUT,c                  ; MIDIデータの1bit送信
            dec     bit_cnt
            goto    int_end

send_strt   ;; 送信準備OK
            jnb     D_STAT,int_end              ; 送信データが無ければなにもしない
            mov     send_buf,midi_byt           ; データを送信バッファに退避
            clrb    D_STAT                      ; 次の送信データ取得を可とする
            cje     send_buf,#DT_PREF1,dt_init  ; dtウェイト?
            cje     send_buf,#DT_PREF2,dt_init

            ;; Start Bit
            clrb    MIDI_OUT                    ; Start
            dec     bit_cnt                     ; bit_cnt = 8
            clrb    MSG_LED                     ; MSG_LED点灯
            goto    int_end

send_stop   ;; Stop Bit
            setb    MIDI_OUT                    ; Stop
            mov     bit_cnt,#9                  ; 送信準備OK
            setb    MSG_LED                     ; MSG_LED消灯
            goto    int_end

dt_init     ;; dtのウェイト処理 play_dt*32*dt(us)
            mov     dt_cnt,play_dt
            mov     bit_cnt,#10                 ; dtウェイト中
dt_wait
            djnz    dt_cnt,int_end              ; 32*play_dt(us)毎に
            mov     dt_cnt,play_dt
            sub     dt[1],#1                    ; dt-- (dt回のウェイト)
            jc      dt_end_chk
            dec     dt[0]
dt_end_chk
            cjne    dt[0],#0,int_end
            cjne    dt[1],#0,int_end
            dec     bit_cnt                     ; dt=0ならwait終了(bit_cnt = 9)

int_end     ;; ----- 割り込み終了処理 -----
			mov		status,stsave				; STATUS戻す
			mov		w,wsave						; W戻す
            retfie                              ; 復帰


;;;;;;;;;;;; バッファ書き込みサブルーチン write_buf ;;;;;;;;;;;;
write_buf
            mov     fsrsave,fsr                 ; fsr退避
            mov     fsr,write_ptr
            mov     indirect,midi_byt           ; data_bufに1byte書き込み
            mov     fsr,fsrsave
            cjb     write_ptr,#BUF_ADR + BUF_LEN - 1,inc_wptr   ; バッファ終端ならptrを先頭に戻す
            mov     write_ptr,#BUF_ADR
            cje     write_ptr,read_ptr,buf_err
            ret
inc_wptr
            inc     write_ptr                   ; 終端でなければptr++
            cje     write_ptr,read_ptr,buf_err
            ret

buf_err     ;; バッファオーバーフロー ** for debug **
            clrb    ERR_LED                     ; ERR_LED点灯、処理は続行
            ret


;;;;;;;;;;;; main ;;;;;;;;;;;;
main
            ;; I/Oポート設定
            mov     !ra,#00000000b              ; MIDI_OUT,LEDを出力に
            mov     !rb,#10001111b              ; MIDI_IN,SW_REC,SW_PLAY,SW_STOP,SDAを入力に、SCL,LEDを出力に
            mov     ra,#00000111b               ; MIDI_OUTを論理「1」に、LED消灯
            mov     rb,#00110000b               ; LED消灯

            ;; option設定
            setb    rp0
            mov     option,#00001000b           ; RBPU=enable,INT=down_edg,PS=none
            clrb    rp0
            mov     ctrl_byte,#10100000b        ; コントロールバイト

            call    get_cpn                     ; 実装メモリチップ数を調べる
            cjne    mem_cpn,#0ffh,init
            clrb    ERR_LED                     ; メモリなし
no_mem
            goto    no_mem

init        ;; 初期化
            mov     intcon,#00100000b           ; タイマ0割り込み許可、グローバル割り込み禁止
            mov     write_ptr,#BUF_ADR          ; バッファptr設定
            mov     read_ptr,#BUF_ADR
            mov     dt_cnt,#DT_UNIT             ; dt初期化
            mov     play_dt,#DT_UNIT
            clr     dt[0]
            clr     dt[1]
            clr     mem_adr[0]
            clr     mem_adr[1]
            clr     flags
            clr     csb
            or      ra,#00000011b               ; ERR_LED以外は消灯
            or      rb,#00110000b

            mov     ctrl_byte,#10100000b        ; コントロールバイト

sw_wait     ;; SW入力待ち
            movb    MIDI_OUT,MIDI_IN            ; MIDI THRU
            movb    MSG_LED,MIDI_IN
sw_wait_rec
            jb      SW_REC,sw_wait_play         ; REC+STOPスイッチONでRecord開始
            jb      SW_STOP,sw_wait
            call    key_up
            goto    rec_strt
sw_wait_play
            jb      SW_PLAY,sw_wait_stop        ; PLAYスイッチONでPlay開始
            call    key_up
            goto    play_strt
sw_wait_stop
            jb      SW_STOP,sw_wait             ; STOPスイッチONの間はMIDITHRU停止(=MIDI OffLine)
            setb    MIDI_OUT
            goto    sw_wait_rec

rec_strt    ;; ----- Record開始 -----
            setb    ERR_LED                     ; ERR_LED消灯
            clrb    MODE                        ; MODE = REC
            clrb    REC_LED                     ; REC_LED点灯
            mov     bit_cnt,#10                 ; 受信準備OK
            setb    gie                         ; グローバル割り込み許可
rec_loop
            call    write_mem                   ; バッファにあるデータをメモリに書き込む
            jb      ERR_DO,rec_end              ; 無音(dt)＞32us*DT_UNIT*ffffh then REC終了
            jb      ERR_MO,error                ; メモリオーバーフロー?
            jb      SW_STOP,rec_loop            ; STOPキー押下?

rec_end     ;; REC終了
            clrb    gie                         ; then グローバル割り込み禁止
            call    key_up
            mov     midi_byt,#EOD
            call    write_buf
            call    write_mem                   ; #EOD 書き込み
            jb      ERR_MO,error
            call    i2c_stop
            goto    init


play_strt   ;; ----- Play開始 -----
            setb    ERR_LED                     ; ERR_LED消灯
            setb    MODE                        ; MODE = PLAY
            clrb    D_STAT                      ; データEmpty
            clrb    PLAY_LED                    ; PLAY_LED点灯
            clrb    IN_PAGE
            mov     bit_cnt,#9                  ; 送信準備OK
            setb    gie                         ; グローバル割り込み許可

play_loop
            jnb     SW_STOP,play_end            ; STOPキー押下?
            jnb     SW_PLAY,spd_up              ; PLAYキーで演奏を速く
            jnb     SW_REC,spd_dwn              ; RECキーで演奏を遅く
            jb      D_STAT,play_loop            ; 割り込みルーチンでデータを読み出すまでは何もしない
            call    read_mem
            jb      ERR_MO,error                ; End of Memory
            mov     midi_byt,rw_dat
            setb    D_STAT                      ; midi_bytへのデータ設定完了

            cje     rw_dat,#EOD,play_end        ; End of Data
            cje     rw_dat,#DT_PREF2,dt_set2    ; dt指定
            cje     rw_dat,#DT_PREF1,dt_set1
            goto    play_loop

spd_up      ;; テンポを速く
            cjbe    play_dt,#TEMPO_MIN,spd_end
            sub     play_dt,#SPD_STEP
            goto    spd_end

spd_dwn     ;; テンポを遅く
            cjae    play_dt,#TEMPO_MAX,spd_end
            add     play_dt,#SPD_STEP
spd_end
            call    key_up
            goto    play_loop

dt_set1     ;; dt処理
            clr     dt[0]                       ; dt[0][1]にdt値をセット
            goto    dt_set11
dt_set2
            call    read_mem
            jb      ERR_MO,error
            mov     dt[0],rw_dat
dt_set11
            call    read_mem
            jb      ERR_MO,error
            mov     dt[1],rw_dat
            goto    play_loop

error       ;; エラー
            clrb    ERR_LED                     ; ERR_LED点灯

play_end    ;; PLAY終了
            clrb    gie                         ; グローバル割り込み禁止
            call    i2c_stop                    ; sequential read 終了
            call    key_up
            goto    init



;;;;;;;;;;;; key_up ; スイッチ(SW_REC,SW_PLAY,SW_STOP)が離されるまで待つ ;;;;;;;;;;;;
key_up
            mov     cnt,#50
            call    wait100
            jnb     SW_REC,key_up
            jnb     SW_PLAY,key_up
            jnb     SW_STOP,key_up
            mov     cnt,#50
            call    wait100
            ret


;;;;;;;;;;;; read_mem ; シリアルメモリから1バイト読み出す ;;;;;;;;;;;;
;;;;;;;;;;;; 読み出し(Read)はSequentialReadシーケンスにより行う
read_mem
            jb      ERR_MO,end_eod                      ; エラー状態：EODを返す
            cjne    mem_adr[0],#0,mem_end_ck
            cjne    mem_adr[1],#0,mem_end_ck
mem_prep    ;; アドレス=0000hならread準備
            call    read_prepare
            goto    read_mem_bt
mem_end_ck
            cjbe    mem_adr[0],#MEM_MAXH,read_mem_bt    ; メモリ終端?
            call    i2c_nac                             ; 終端ならシーケンシャルRead終了
            call    i2c_stop
            cjae    csb,mem_cpn,end_eod                 ; メモリデバイスがさらに存在するか?
            clr     mem_adr[0]                          ; then adr=0, csb++
            clr     mem_adr[1]
            inc     csb
            call    set_csb
            goto    mem_prep
end_eod
            mov     rw_dat,#EOD
            setb    ERR_MO                      ; End of memory
            ret
read_mem_bt ;; 1バイトread
            call    i2c_read
            call    i2c_ack
            call    adr_inc                     ; アドレス++
read_mem_end
            ret


;;;;;;;;;;;; write_mem ; バッファ上に溜まったデータをシリアルメモリに転送する ;;;;;;;;;;;;
;;;;;;;;;;;; 転送(Write)はPageWriteシーケンスにより最大32バイトまとめて書き込む
write_mem
            cje     read_ptr,write_ptr,w_mem_end    ; データなし
            cjbe    mem_adr[0],#MEM_MAXH,w_mem_bt   ; メモリ終端?
            cjae    csb,mem_cpn,w_mem_err           ; メモリデバイスがさらに存在するか?
            clr     mem_adr[0]                      ; then adr=0, csb++
            clr     mem_adr[1]
            inc     csb
            call    set_csb
w_mem_bt
            jb      IN_PAGE,w_001
            call    write_prepare               ; 書き込み準備(pagewrite毎に)
            setb    IN_PAGE
w_001
            mov     fsr,read_ptr
            mov     rw_dat,indirect             ; 1byteを転送
            call    i2c_write
            call    adr_inc
            cjb     read_ptr,#BUF_ADR + BUF_LEN - 1,inc_rptr    ; バッファ終端ならptrを先頭に戻し、
            call    i2c_stop                                    ; pagewrite完了
            mov     read_ptr,#BUF_ADR
            clrb    IN_PAGE
            goto    write_mem
inc_rptr
            inc     read_ptr                    ; バッファ終端でなければptr++
            goto    write_mem
w_mem_err
            setb    ERR_MO
w_mem_end
            jb      IN_PAGE,w_rest              ; pagewrite中ならStop発行でメモリチップ内の書き込みサイクルを
            ret                                 ; 実行させてから終了
w_rest
            call    i2c_stop
            clrb    IN_PAGE
            ret


;;;;;;;;;;;; wait100 ; 100μs * cnt 時間を消費 ;;;;;;;;;;;;
wait100
            mov     work,#WAIT_CNT
loop100
            decsz   work
            goto    loop100
            decsz   cnt
            goto    wait100
            ret

;; ******** I2Cインタフェースサブルーチン ********

; i2c_start
; Start Conditionを出力する

i2c_start
        clrb    gie
            setb    rp0                         ; Bank1
            clrb    SDAIO                       ; SDAを出力にする
            clrb    rp0                         ; Bank0
        setb    gie

            clrb    SCL                         ; SCL=L (TLOW = 1600ns)
            nop
            setb    SDA                         ; SDA=H
            nop                                 ; (TSU:DAT = 800ns)
            setb    SCL                         ; SCL=H (THIGH = 1600ns)
            nop                                 ; (TSU:STA = 800ns)
;           nop
            clrb    SDA                         ; SCL=HでSDAがH->L : start condition
            nop                                 ; (THD:STA = 800ns)
;           nop
            clrb    SCL                         ; SCL=L (TLOW > 2000ns)

        clrb    gie
            setb    rp0                         ; Bank1
            setb    SDAIO                       ; SDAを開放(入力)にする
            clrb    rp0                         ; Bank0
        setb    gie
            ret


; i2c_stop
; Stop Conditionを出力する

i2c_stop
        clrb    gie
            setb    rp0                         ; Bank1
            clrb    SDAIO                       ; SDAを出力にする
            clrb    rp0                         ; Bank0
        setb    gie

            clrb    SCL                         ; SCL=L (TLOW = 1600ns)
            nop
            clrb    SDA                         ; SDA=L
            nop                                 ; (TSU:DAT = 800ns)
            setb    SCL                         ; SCL=H (THIGH = 1600ns)
            nop                                 ; (TSU:STO = 800ns)
;           nop
            setb    SDA                         ; SCL=HでSDAがL->H : stop condition
            nop
            clrb    SCL                         ; SCL=L (TLOW > 2000ns)

        clrb    gie
            setb    rp0                         ; Bank1
            setb    SDAIO                       ; SDAを開放(入力)にする
            clrb    rp0                         ; Bank0
        setb    gie
            ret


; i2c_write (マスタ・トランスミッタ)
; 1バイトのデータを出力する
; rw_datの内容は破壊される
; ACK_BIT(rw_dat.0)に実行結果(0:ACK / 1:NAK)が設定される


i2c_write
        clrb    gie
            setb    rp0                         ; Bank1
            clrb    SDAIO                       ; SDAを出力にする
            clrb    rp0                         ; Bank0
        setb    gie
            mov     cnt,#8                      ; カウンタセット
send_bit
            rl      rw_dat
            movb    SDA,c                       ; dataの7bit目から送信 (TSU:DAT = 800～1600ns)
            nop
;           nop
            setb    SCL                         ; SCL=H (THIGH = 800ns)
            nop
;           nop
            clrb    SCL                         ; SCL=L (TLOW = 2400～4000ns)
            djnz    cnt,send_bit                ; 6～0bit繰り返し

            ;; get acknowledge bit
        clrb    gie
            setb    rp0                         ; Bank1
            setb    SDAIO                       ; SDAを開放(入力)にする
            clrb    rp0                         ; Bank0
        setb    gie
            setb    SCL                         ; SCL=H (THIGH = 2400ns)
            nop
            movb    ACK_BIT,SDA                 ; ACK_BIT = ACK/NO ACK
            clrb    SCL
            ret

; i2c_read (マスタ・レシーバ)
; 1バイトのデータを読み込む
; 受信データはrw_datに格納される
i2c_read
            mov     cnt,#8                      ; カウンタセット
read_bit
            setb    SCL                         ; SCL=H (THIGH = 2800ns)
            nop                                 ; (TAA > 2000ns)
            movb    c,SDA                       ; 1bit受信
            rl      rw_dat
            clrb    SCL                         ; SCL=L (TLOW = 1600ns)
            djnz    cnt,read_bit                ; 7-0bit繰り返し
            ret

; レシーバからACK/NACを送信する
i2c_ack
        clrb    gie
            setb    rp0                         ; Bank1
            clrb    SDAIO                       ; SDAを出力にする
            clrb    rp0                         ; Bank0
        setb    gie
            clrb    SDA                         ; ACK (TSU:DAT = 800ns)
            nop
            setb    SCL                         ; SCL=H (THIGH = 800ns)
            nop
;           nop
            clrb    SCL
        clrb    gie
            setb    rp0                         ; Bank1
            setb    SDAIO                       ; SDAを開放(入力)にする
            clrb    rp0                         ; Bank0
        setb    gie
            ret

i2c_nac
        clrb    gie
            setb    rp0                         ; Bank1
            clrb    SDAIO                       ; SDAを出力にする
            clrb    rp0                         ; Bank0
        setb    gie
            setb    SDA                         ; NO ACK (TSU:DAT = 800ns)
            nop
            setb    SCL                         ; SCL=H (THIGH = 800ns)
            nop
;           nop
            clrb    SCL
        clrb    gie
            setb    rp0                         ; Bank1
            setb    SDAIO                       ; SDAを開放(入力)にする
            clrb    rp0                         ; Bank0
        setb    gie
            ret


;; ******** 24LCxxアクセスサブルーチン ********

; set_csb
; csbをctrl_byteのメモリチップセレクトbitsに再設定する
set_csb
            mov     work,csb
            rl      work
            and     work,#00001110b
            and     ctrl_byte,#11110001b
            or      ctrl_byte,work
            ret


; send_ctrl
; START および Control Byte 送信 + ACK待ち
send_ctrl
            call    i2c_start
            mov     rw_dat,ctrl_byte
            call    i2c_write
            jb      ACK_BIT,send_ctrl           ; if NO ACK then retry
            ret


; adr_inc
; アドレスインクリメント
adr_inc
            incsz   mem_adr[1]                  ; address++
            ret
            inc     mem_adr[0]
            ret

; get_cpn
; I2Cバスをスキャンし、実装されているメモリチップ数を調べる
get_cpn
            setb    R_W
            clr     csb
            clr     mem_cpn
cpn_loop
            call    set_csb                     ; csbを0から7まで
            call    i2c_start                   ; Readを行い、結果がNACになるまでスキャン
            mov     rw_dat,ctrl_byte
            call    i2c_write
            call    i2c_stop
            jb      ACK_BIT,cpn_end
            inc     csb
            cjbe    csb,#7,cpn_loop
cpn_end
            mov     mem_cpn,csb
            dec     mem_cpn                     ; 0～7ならチップ接続あり、FFhならエラー
            ret


; write_prepare
;
write_prepare
            clrb    R_W                         ; r/w = 0 (write)
            call    send_ctrl                   ; START and control byte
            mov     rw_dat,mem_adr[0]           ; address high
            call    i2c_write
            mov     rw_dat,mem_adr[1]           ; address low
            call    i2c_write
            ret

; read_prepare
;
read_prepare
            clrb    R_W                         ; r/w = 0 (write)
            call    send_ctrl                   ; START and control byte
            mov     rw_dat,mem_adr[0]           ; address high
            call    i2c_write
            mov     rw_dat,mem_adr[1]           ; address low
            call    i2c_write
            setb    R_W                         ; r/w = 1 (read)
            call    send_ctrl                   ; START and control byte
            ret

; byte_write
;
byte_write
            call    write_prepare
            mov     rw_dat,mem_io_dat
            call    i2c_write
            call    i2c_stop
            ret



;;;;;;;;;;;;;;;;;;; おしまい ;;;;;;;;;;;;;;;;;;;;;;
