Tiny BASIC for Z-80

東大版2K Tiny BASICの文法メモと移植版ソースを相当な年数を経て発掘したので無くさないようにクラウド化する。Windows95華やかりし頃に作成して以来メンテしていないから内容についての保証はいたしかねる。

Tiny BASIC関連の記事を久しぶりにメンテした関連。東大版2K Tiny BASICも元はPALO ALTO TINY BASICだったはずなので次の記事の文法解説としてはこれで間に合う。Win32でも動く東大版ではないオリジナルのPALO ALTO TYNY BASICコードは次のエントリで掲載。

8080のTinyBASICで遊ぶ その1
https://mzex.wordpress.com/2018/05/26/12977/

※例によって半角の山カッコはWordpressで破壊されるので全角の<>に置換。

Tiny BASIC文法メモ

*コマンド

        NEW
        LIST <値>
        RUN

*ステートメント

        LET <変数>=<式> ※LET は省略可
        PRINT [<リテラル>,式]
        INPUT [<リテラル>],変数]
        GOTO <式>
        GOSUB <式>
        RETURN
        IF <式> <ステートメント|コマンド>
        FOR <変数>=<式> TO <式> [STEP <式>] ~ NEXT <変数>
        STOP
        SYSTEM ※CP/Mに戻る、ウォームリブート。独自拡張。

        マルチステートメントは ';'
        (<ステートメント>;<ステートメント>)

*関数

        RND(<式>)       1~<式>までの値を返す
        ABS(<式>)       <式>の絶対値を返す
        SIZE            空きメモリのサイズを返す

*演算子

          =             等しい
          #             等しくない
          >             より大きい
          >=            以上
          <             未満
          <=            以下
          +             加算 または 正
          -             減算 または 負
          *             乗算
          /             除算

*変数

        A-Z             単純変数 (一文字のみ)
        @(<式>)         配列変数 (空きメモリの範囲)


*整数

        -32768~32767 (10進)

Z-80版のTiny BASICも発掘されたので同時にクラウド化しておく。アスキー1977年8月号の東大版TinyBASICを秋月電子扱いでお安いシステムロード社製のアブソリュートクロスアセンブラXA80 Ver2.01でアセンブルできように移植して、CP/Mのトランジェントコマンドとしても実行できるようにしたもの。アスキー1977年10月号のバグフィックスパッチも適用済み。RAM領域は秋月電子AKI-80に合わせて8000Hから始まるようにしたので本物のCP/Mだと定義を変更しない場合はRAM48kB以上実装のシステムが必要。実際はシステムロードのCP/M-Simulator Ver.3.21とAKI-80の自作ROM-BIOS上のみで確認しているので実機は未確認。例によって半角の山カッコはWordpressで破壊されるので全角の<>に置換。

[TBCPM.ASM]

;
;
;       PALO ALTO TINY BASIC
;
;
;       FOR Z-80 CP/M VERSION
;
STACK   EQU     0E400H
VTOP    EQU     0E000H
LBUF    EQU     0E337H
MSTK    EQU     0E3A7H
RAMST   EQU     08000H
IENT    EQU     0100H
IFLG    EQU     0FBH
OFLG    EQU     0FBH
OCNT    EQU     0FBH
ITTY    EQU     0FAH
OTTY    EQU     0FAH
IMSK    EQU     002H
OMSK    EQU     001H

;TST    MACRO   STR,ADRS
;       CALL    TEST
;       DB      STR
;       DB      ADRS-$-1
;       ENDM

;CASE   MACRO   STR,ADRS
;       DB      STR
;       DB      (ADRS+8000H) SHR 8
;       DB      ADRS AND 0FFH
;       ENDM

;ENDC   MACRO   ADRS
;       DB      (ADRS+8000H) SHR 8
;       DB      ADRS AND 0FFH
;       ENDM

        ORG     IENT

        CALL    STRUP           ;INITIALIZE
START:
;       DI                      ;START
        LD      SP,STACK
        JP      ENTRY
;
TEST:   EX      (SP),HL         ;TEST CHARACTER
        CALL    SKPBL
        CP      (HL)
        JP      TEST1
;
CRLF:   LD      A,0DH
PUT:    PUSH    AF              ;PUT CHARACTER IN A REG
        JP      TTYO1
;
EEXPR:  CALL    EXPR            ;EXTENDED EXPRESSION
        PUSH    HL
        JP      RST31
;
COMPR:  LD      A,H             ;COMPARE DE AND HL
        CP      D
        RET     NZ
        LD      A,L
        CP      E
        RET
;
SKPBL:  LD      A,(DE)          ;SKIP BLANK
        CP      ' '
        RET     NZ
        INC     DE
        JP      SKPBL
;
ENDL:   CALL    TSTSC           ;TEST LINE END
        JP      LERMS
;
TSTV:   CALL    SKPBL           ;TEST VARIABLE
        SUB     40H
        RET     C
        JP      NZ,TSTV1
        INC     DE
        CALL    EXPR0
        ADD     HL,HL
        JP      C,ERROR
        PUSH    DE
        EX      DE,HL
        CALL    RSIZE
        CALL    COMPR
        JP      C,SYSO1
        LD      HL,VTOP
        CALL    DIFF
        POP     DE
        RET
;
TSTV1:  CP      1BH             ;'A' 'Z' ?
        CCF
        RET     C               ;NO
        INC     DE
        LD      HL,VTOP
        RLCA
        ADD     A,L
        LD      L,A
        LD      A,00H
        ADC     A,H
        LD      H,A
        RET
;
TEST1:  INC     HL
        JP      Z,TSTEQ
        PUSH    BC
        LD      C,(HL)          ;FETCH SKIP NUMBER
        LD      B,00H
        ADD     HL,BC
        POP     BC
        DEC     DE
TSTEQ:  INC     DE
        INC     HL
        EX      (SP),HL
        RET
;
GINT:   LD      HL,0000H        ;GET INTEGER
        LD      B,H
        CALL    SKPBL
GETI1:  CP      30H
        RET     C               ;RETURN INTEGER IN HL
        CP      3AH
        RET     NC
        LD      A,0F0H
        AND     H               ;IF INTEGER>#0FFF ERROR
        JP      NZ,ERROR
        INC     B
        PUSH    BC
        LD      B,H             ;HL HL+10
        LD      C,L
        ADD     HL,HL
        ADD     HL,HL
        ADD     HL,BC
        ADD     HL,HL
        LD      A,(DE)          ;HL HL+(DE)&#0F
        INC     DE
        AND     0FH
        ADD     A,L
        LD      L,A
        LD      A,00H
        ADC     A,H
        LD      H,A
        POP     BC
        LD      A,(DE)
        JP      P,GETI1
;
ERROR:  PUSH    DE
ERR1:   LD      DE,HOWMS
        JP      LEMS1
;
HOWMS:  DB      'HOW?'
        DB      0DH
OKMES:  DB      'OK'
        DB      0DH
WHTMS:  DB      'WHAT?'
        DB      0DH
SRYMS:  DB      'SORRY'
        DB      0DH
;
ENTRY:  CALL    CRLF
        LD      DE,OKMES
        SUB     A
        CALL    MSG
        LD      HL,00CBH
        LD      (COBJ),HL
        LD      HL,0000H
        LD      (FCNTR),HL
        LD      (RSTCK),HL
;
GETC:   LD      A,'>'           ;'>' PROMPT
        CALL    GETL
        PUSH    DE              ;SAVE LINE END
        LD      DE,LBUF
        CALL    GINT            ;HL=LABEL NUMBER
        CALL    SKPBL
        LD      A,H
        OR      L
        POP     BC              ;RECOVER LINE END
        JP      Z,KWCPR         ;IF LABEL NUMBER=0 COMMAND
INSRT:  DEC     DE
        LD      A,H
        LD      (DE),A
        DEC     DE
        LD      A,L
        LD      (DE),A
        PUSH    BC              ;SAVE LINE BOTTOM
        PUSH    DE              ;SAVE LINE TOP
        LD      A,C
        SUB     E
        PUSH    AF              ;SAVE LINE LENGTH
        CALL    SRCH
        PUSH    DE              ;SAVE CURRENT LINE
        JP      NZ,MOVE
        PUSH    DE              ;EXIST SAME LINE
        CALL    SKIPL
        POP     BC
        LD      HL,(OBTM)
        CALL    TRNSF           ;ERASE OLD LINE
        LD      H,B
        LD      L,C
        LD      (OBTM),HL       ;UPDATE BOTTOM POINTER
MOVE:   POP     BC              ;RECOVER CURRENT LINE
        LD      HL,(OBTM)
        POP     AF              ;RECOVER LINE LENGTH
        PUSH    HL              ;SAVE CURRENT BOTTOM
        CP      03H
        JP      Z,START         ;LENGTH=3? RESTART
        ADD     A,L             ;COMPUTE MOVE BOTTOM
        LD      L,A
        LD      A,00H   
        ADC     A,H
        LD      H,A
        LD      DE,VTOP
        CALL    COMPR
        JP      NC,SYSOF
        LD      (OBTM),HL
        POP     DE              ;RECOVER CURRENT BOTTOM
        CALL    TR2             ;MOVE FOR INSERTION
        POP     DE              ;RECOVER LINE TOP
        POP     HL              ;RECOVER LINE BOTTOM
        CALL    TRNSF           ;INSERT LINE
        JP      GETC
;
CMDKW:
;       CASE    '''LIST''',LIST
        DB      'LIST'
        DB      (LIST+8000H) SHR 8
        DB      LIST AND 0FFH
;       CASE    '''RUN''',RUN
        DB      'RUN'
        DB      (RUN+8000H) SHR 8
        DB      RUN AND 0FFH
;       CASE    '''NEW''',NEW
        DB      'NEW'
        DB      (NEW+8000H) SHR 8
        DB      NEW AND 0FFH
;       CASE    '''SYSTEM''',SYSTM
        DB      'SYSTEM'
        DB      (SYSTM+8000H) SHR 8
        DB      SYSTM AND 0FFH
STMKW:
;       CASE    '''NEXT''',NEXT
        DB      'NEXT'
        DB      (NEXT+8000H) SHR 8
        DB      NEXT AND 0FFH
;       CASE    '''LET''',LET
        DB      'LET'
        DB      (LET+8000H) SHR 8
        DB      LET AND 0FFH
;       CASE    '''IF''',IFSTM
        DB      'IF'
        DB      (IFSTM+8000H) SHR 8
        DB      IFSTM AND 0FFH
;       CASE    '''GOTO''',GOTO
        DB      'GOTO'
        DB      (GOTO+8000H) SHR 8
        DB      GOTO AND 0FFH
;       CASE    '''GOSUB''',GOSUB
        DB      'GOSUB'
        DB      (GOSUB+8000H) SHR 8
        DB      GOSUB AND 0FFH
;       CASE    '''RETURN''',RETRN
        DB      'RETURN'
        DB      (RETRN+8000H) SHR 8
        DB      RETRN AND 0FFH
;       CASE    '''REM''',REM
        DB      'REM'
        DB      (REM+8000H) SHR 8
        DB      REM AND 0FFH
;       CASE    '''FOR''',FOR
        DB      'FOR'
        DB      (FOR+8000H) SHR 8
        DB      FOR AND 0FFH
;       CASE    '''INPUT''',INPUT
        DB      'INPUT'
        DB      (INPUT+8000H) SHR 8
        DB      INPUT AND 0FFH
;       CASE    '''PRINT''',PRINT
        DB      'PRINT'
        DB      (PRINT+8000H) SHR 8
        DB      PRINT AND 0FFH
;       CASE    '''STOP''',STOP
        DB      'STOP'
        DB      (STOP+8000H) SHR 8
        DB      STOP AND 0FFH
;       ENDC    CMDER
        DB      (CMDER+8000H) SHR 8
        DB      CMDER AND 0FFH
FNKW:
;       CASE    '''RND''',RND
        DB      'RND'
        DB      (RND+8000H) SHR 8
        DB      RND AND 0FFH
;       CASE    '''ABS''',FNABS
        DB      'ABS'
        DB      (FNABS+8000H) SHR 8
        DB      FNABS AND 0FFH
;       CASE    '''SIZE''',RSIZE
        DB      'SIZE'
        DB      (RSIZE+8000H) SHR 8
        DB      RSIZE AND 0FFH
;       ENDC    FACT2
        DB      (FACT2+8000H) SHR 8
        DB      FACT2 AND 0FFH
KWTO:
;       CASE    '''TO''',FORTO
        DB      'TO'
        DB      (FORTO+8000H) SHR 8
        DB      FORTO AND 0FFH
;       ENDC    LERMS
        DB      (LERMS+8000H) SHR 8
        DB      LERMS AND 0FFH
KWSTP:
;       CASE    '''STEP''',STEP
        DB      'STEP'
        DB      (FSTEP+8000H) SHR 8
        DB      FSTEP AND 0FFH
;       ENDC    FSTP1
        DB      (FSTP1+8000H) SHR 8
        DB      FSTP1 AND 0FFH
ROPKW:
;       CASE    '''>=''',LGE
        DB      '>='
        DB      (LGE+8000H) SHR 8
        DB      LGE AND 0FFH
;       CASE    '''#''',LNE
        DB      '#'
        DB      (LNE+8000H) SHR 8
        DB      LNE AND 0FFH
;       CASE    '''>''',LGT
        DB      '>'
        DB      (LGT+8000H) SHR 8
        DB      LGT AND 0FFH
;       CASE    '''=''',LEQ
        DB      '='
        DB      (LEQ+8000H) SHR 8
        DB      LEQ AND 0FFH
;       CASE    '''<=''',LLE
        DB      '<=,'
        DB      (LLE+8000H) SHR 8
        DB      LLE AND 0FFH
;       CASE    '''<''',LLT
        DB      '<'
        DB      (LLT+8000H) SHR 8
        DB      LLT AND 0FFH
;       ENDC    NOROP
        DB      (NOROP+8000H) SHR 8
        DB      NOROP AND 0FFH
;
KWCPR:  LD      HL,CMDKW-1
NXTKW:  CALL    SKPBL
        PUSH    DE              ;SAVE LINE TOP
KWC1:   LD      A,(DE)
        INC     DE
        CP      2EH             ;'.'
        JP      Z,KWSRT
        INC     HL
        CP      (HL)
        JP      Z,KWC1
        LD      A,7FH
        DEC     DE
        CP      (HL)
        JP      C,EXEQT         ;WHOLE KEYWORD?
KWSK1:  INC     HL
        CP      (HL)
        JP      NC,KWSK1
        INC     HL
        POP     DE              ;RECOVER LINE TOP
        JP      NXTKW
;
KWSRT:  LD      A,7FH
KWSK2:  INC     HL
        CP      (HL)
        JP      NC,KWSK2
;
EXEQT:  LD      A,(HL)          ;FTECH EXEC ADDR HIGH
        INC     HL
        LD      L,(HL)          ;FETCH EXEC ADDR LOW
        AND     7FH
        LD      H,A
        POP     AF              ;DUMMY POP UP
        JP      (HL)            ;GOTO EACH ROUTINE
;
SYSTM:  CALL    MONBK
        JP      IFST2
;
NEW:    CALL    TSCR2
        LD      HL,OTOP
        LD      (OBTM),HL
STOP:   CALL    TSCR2
        JP      START
;
RUN:    CALL    TSCR2
        LD      DE,OTOP
;
RUN1:   LD      HL,0000H
        CALL    SRCH1           ;SEARCH MINIMUM LABEL OR
        JP      C,START
;
RUN2:   EX      DE,HL
        LD      (COBJ),HL
        EX      DE,HL
        INC     DE              ;SKIP LABEL
        INC     DE
;
NXTGO:  CALL    BREAK
        LD      HL,STMKW-1
        JP      NXTKW
;
GOTO:   CALL    EEXPR
        PUSH    DE              ;SAVE LINE POINTER
        CALL    TSCR2
        CALL    SRCH
        JP      NZ,ERR1
        POP     AF              ;DUMMY POP
        JP      RUN2
;
LIST:   CALL    GINT
        CALL    TSCR2
        CALL    SRCH
LISTL:  JP      C,START         ;IF EMPTY OR EOF RESTART
        CALL    WLINE
        CALL    BREAK
        CALL    SRCH1
        JP      LISTL
;
PRINT:  LD      C,06H
;
;       TST     ''';''',PRNT1
        CALL    TEST
        DB      ';'
        DB      PRNT1-$-1
        CALL    CRLF
        JP      NXTGO
;
PRNT1:
;       TST     0DH,PRNT2
        CALL    TEST
        DB      0DH
        DB      PRNT2-$-1
        CALL    CRLF
        JP      RUN1
;
PRNT2:
;       TST     '''#''',PRNT3
        CALL    TEST
        DB      '#'
        DB      PRNT3-$-1
        CALL    EEXPR
        LD      C,L
        JP      PRNT4
PRNT3:  CALL    PR10
        JP      PRNT6
PRNT4:
;       TST     ''',''',PRNT5
        CALL    TEST
        DB      ','
        DB      PRNT5-$-1
        CALL    TSTSC
        JP      PRNT2
;
PRNT5:  CALL    CRLF
        JP      ENDL
;
PRNT6:  CALL    EEXPR
        PUSH    BC
        CALL    WINT
        POP     BC
        JP      PRNT4
;
GOSUB:  CALL    PSHV
        CALL    EEXPR
        PUSH    DE              ;SAVE OBJ POINTER
        CALL    SRCH
        JP      NZ,ERR1
        LD      HL,(COBJ)
        PUSH    HL
        LD      HL,(RSTCK)
        PUSH    HL
        LD      HL,0000H
        LD      (FCNTR),HL
        ADD     HL,SP
        LD      (RSTCK),HL
        JP      RUN2
;
RETRN:  CALL    TSCR2
        LD      HL,(RSTCK)
        LD      A,H
        OR      L
        JP      Z,LERMS ;IF RSTACKTOP=0 ERROR
        LD      SP,HL
        POP     HL
        LD      (RSTCK),HL
        POP     HL
        LD      (COBJ),HL
        POP     DE
        CALL    POPV
        JP      ENDL
;
FOR:    CALL    PSHV
        CALL    LTSUB
        DEC     HL
        LD      (FCNTR),HL
        LD      HL,KWTO-1
        JP      NXTKW
;
FORTO:  CALL    EEXPR
        LD      (FTOV),HL
        LD      HL,KWSTP-1
        JP      NXTKW
;
FSTEP:  CALL    EEXPR
        JP      FOR0
;
FSTP1:  LD      HL,0001H
FOR0:   LD      (FSTPV),HL
        LD      HL,(COBJ)
        LD      (FLABL),HL
        EX      DE,HL
        LD      (FOBJ),HL
        LD      BC,000AH
        LD      HL,(FCNTR)
        EX      DE,HL
        LD      H,B
        LD      L,B
        ADD     HL,SP
        DB      3EH             ;(SKIP NEXT INSTRUCTION)
;
FOR3:   ADD     HL,BC
        LD      A,(HL)
        INC     HL
        OR      (HL)
        JP      Z,FOR10
        LD      A,(HL)
        DEC     HL
        CP      D
        JP      NZ,FOR3
        LD      A,(HL)
        CP      E
        JP      NZ,FOR3
        EX      DE,HL
        LD      HL,0000H
        ADD     HL,SP
        LD      B,H
        LD      C,L
        LD      HL,000AH
        ADD     HL,DE
        CALL    TR2
        LD      SP,HL
FOR10:  LD      HL,(FOBJ)
        EX      DE,HL
        JP      ENDL
;
NEXT:   CALL    TSTV
        JP      C,LERMS
        LD      (NCNTR),HL
NEXT1:  PUSH    DE
        EX      DE,HL
        LD      HL,(FCNTR)
        LD      A,H
        OR      L
        JP      Z,LEMS0
        CALL    COMPR
        JP      Z,NEXT2 ;NEXT VAR.=FORVAR.?
        POP     DE
        CALL    POPV
        LD      HL,(NCNTR)
        JP      NEXT1
NEXT2:  LD      E,(HL)
        INC     HL
        LD      D,(HL)          ;DE OLD FOR VALUE
        LD      HL,(FSTPV)
        PUSH    HL              ;HL STEP VALUE
        ADD     HL,DE
        EX      DE,HL           ;DE NEW FOR VALUE
        LD      HL,(FCNTR)
        LD      (HL),E
        INC     HL
        LD      (HL),D          ;FOR VAR. NEW VALUE
        LD      HL,(FTOV)
        POP     AF
        OR      A
        JP      P,NEXT4
        EX      DE,HL
NEXT4:  CALL    CMINT
        POP     DE
        JP      C,NEXT5
        LD      HL,(FLABL)
        LD      (COBJ),HL
        LD      HL,(FOBJ)
        EX      DE,HL
        JP      ENDL
;
NEXT5:  CALL    POPV
        JP      ENDL
;
REM:    LD      HL,0000H
        JP      IFST2
;
IFSTM:  CALL    EEXPR
        LD      A,H
        OR      L
        JP      NZ,NXTGO
IFST2:  CALL    SKPL2
        JP      NC,RUN2
        JP      START
;
ERRIN:  LD      HL,(NCNTR)
        LD      SP,HL
        POP     HL
        LD      (COBJ),HL
        POP     DE
        POP     DE
INPUT:  PUSH    DE
        CALL    PR10
        JP      INPT2
;
INPT1:  CALL    TSTV
        JP      C,INPT6
        JP      INPT4
INPT2:  PUSH    DE
        CALL    TSTV
        JP      C,LERMS
        LD      A,(DE)
        LD      C,A
        SUB     A
        LD      (DE),A
        POP     DE
        CALL    MSG
        LD      A,C
        DEC     DE
        LD      (DE),A
;
INPT4:  PUSH    DE
        EX      DE,HL
        LD      HL,(COBJ)
        PUSH    HL
        LD      HL,INPUT
        LD      (COBJ),HL
        LD      HL,0000H
        ADD     HL,SP
        LD      (NCNTR),HL
        PUSH    DE
        LD      A,':'
        CALL    GETL
        LD      DE,LBUF
        CALL    EEXPR
        POP     DE
        EX      DE,HL
        LD      (HL),E
        INC     HL
        LD      (HL),D
        POP     HL
        LD      (COBJ),HL
        POP     DE
;
INPT6:  POP     AF
;       TST     ''',''',LTEND
        CALL    TEST
        DB      ','
        DB      LTEND-$-1
        JP      INPUT
;
CMDER:  LD      A,(DE)
        CP      0DH
        JP      Z,ENDL
;
LET:    CALL    LTSUB
;       TST     ''',''',LTEND
        CALL    TEST
        DB      ','
        DB      LTEND-$-1
        JP      LET
LTEND:  JP      ENDL
;
RST31:  LD      HL,ROPKW-1
        JP      NXTKW
;
LGE:    CALL    IFEXQ
        RET     C
        LD      L,A
        RET
;
LNE:    CALL    IFEXQ
        RET     Z
        LD      L,A
        RET
;
LGT:    CALL    IFEXQ
        RET     Z
        RET     C
        LD      L,A
        RET
;
LLE:    CALL    IFEXQ
        LD      L,A
        RET     Z
        RET     C
        LD      L,H
        RET
;
LEQ:    CALL    IFEXQ
        RET     NZ
        LD      L,A
        RET
;
LLT:    CALL    IFEXQ
        RET     NC
        LD      L,A
        RET
;
NOROP:  POP     HL
        RET
;
IFEXQ:  LD      A,C             ;SAVE C
        POP     HL              ;EXCHANGE STACK TOP 2
        POP     BC
        PUSH    HL
        PUSH    BC
        LD      C,A             ;RECOVER C
        CALL    EXPR
        EX      DE,HL           ;EXCANGE LP AND 1 AND 2
        EX      (SP),HL
        CALL    CMINT
        POP     DE
        LD      HL,0000H
        LD      A,01H
        RET                     ;SET C AND Z FF
;
EXPR:
;       TST     '''-''',EXPR1
        CALL    TEST
        DB      '-'
        DB      EXPR1-$-1
        LD      HL,0000H
        JP      NEGA1
;
EXPR1:
;       TST     '''+''',EXPR3
        CALL    TEST
        DB      '+'
        DB      EXPR3-$-1
EXPR3:  CALL    TERM
;
EXPR2:
;       TST     '''+''',NEGA0
        CALL    TEST
        DB      '+'
        DB      NEGA0-$-1
        PUSH    HL              ;SAVE VALUE
        CALL    TERM
ADDDBL: EX      DE,HL
        EX      (SP),HL         ;XCHANGE LP AND 1 AND 2
        LD      A,H
        XOR     D
        LD      A,D
        ADD     HL,DE
        POP     DE              ;RECOVER LINE POINTER
        JP      M,EXPR2         ;CHECK OVERFLOW
        XOR     H
        JP      P,EXPR2
        JP      ERROR
;
NEGA0:
;       TST     '''-''',EXPRT
        CALL    TEST
        DB      '-'
        DB      EXPRT-$-1
;
NEGA1:  PUSH    HL              ;SAVE VALUE
        CALL    TERM
        CALL    TWSCP
        JP      ADDDBL
;
TERM:   CALL    FACTR
;
MULT:
;       TST     '''*''',DIV
        CALL    TEST
        DB      '*'
        DB      DIV-$-1
        PUSH    HL              ;SAVE FACTOR VALUE
        CALL    FACTR
        LD      B,00H           ;SET SIGN PUS
        CALL    ABS
        EX      DE,HL
        EX      (SP),HL
        CALL    ABS
        LD      A,H
        OR      A
        JP      Z,MULT1
        LD      A,D
        OR      D
        EX      DE,HL
        JP      NZ,ERR1         ;IF BOTH MULTIPLIER>#00FF
MULT1:  LD      A,L
        LD      HL,0000H
        OR      A
        JP      Z,TERM1 ;IF ONE OF MULTIPLIER=0 V
MULTL:  ADD     HL,DE
        JP      C,ERR1          ;OVERFLOW?
        DEC     A
        JP      NZ,MULTL
        JP      TERM1
;
DIV:
;       TST     '''/''',EXPRT
        CALL    TEST
        DB      '/'
        DB      EXPRT-$-1
        PUSH    HL              ;SAVE VALUE
        CALL    FACTR
        LD      B,00H           ;SET SIGN PLUS
        CALL    ABS
        EX      DE,HL
        EX      (SP),HL
        CALL    ABS
        LD      A,D
        OR      E
        JP      Z,ERR1          ;ZERO DIV?
        PUSH    BC              ;SAVE SIGN
        CALL    DIVID
        LD      H,B
        LD      L,C
        POP     BC              ;RECOVER SIGN
TERM1:  POP     DE
        LD      A,H
        OR      A
        JP      M,ERROR         ;OVERFLOW?
        LD      A,B
        OR      A
        CALL    M,TWSCP
        JP      MULT
;
FACTR:  LD      HL,FNKW-1
        JP      NXTKW
;
FACT2:  CALL    TSTV            ;FROM KEY WORD MISMATCH
        JP      C,FNUM
        LD      A,(HL)
        INC     HL
        LD      H,(HL)
        LD      L,A
        RET                     ;RETURN VALUE ON HL
;
FNUM:   CALL    GINT
        LD      A,B
        OR      A
        RET     NZ
;
EXPR0:
;       TST     '''(''',EXPER
        CALL    TEST
        DB      '('
        DB      EXPER-$-1
        CALL    EEXPR
;       TST     ''')''',EXPER
        CALL    TEST
        DB      ')'
        DB      EXPER-$-1
EXPRT:  RET
EXPER:  JP      LERMS
;
RND:    CALL    EXPR0
        LD      A,H
        OR      A
        JP      M,ERROR
        OR      L
        JP      Z,ERROR
        PUSH    DE
        PUSH    HL
        LD      HL,(RWRK)
        LD      A,H
        AND     07H
        LD      H,A
        LD      DE,IENT
        ADD     HL,DE
RND1:   LD      E,(HL)
        INC     HL
        LD      D,(HL)
        LD      (RWRK),HL
        POP     HL
        EX      DE,HL
        PUSH    BC
        CALL    DIVID
        POP     BC
        POP     DE
        INC     HL
        RET
;
FNABS:  CALL    EXPR0
        CALL    ABS
        LD      A,H
        OR      H
        JP      M,ERROR
        RET
;
RSIZE:  LD      HL,(OBTM)       ;VTOP-(CURRENTLB)
        PUSH    DE
        EX      DE,HL
        LD      HL,VTOP
        CALL    DIFF
        POP     DE
        RET
;
DIVID:  PUSH    HL
        LD      L,H
        LD      H,00H
        CALL    DIVSB
        LD      B,C
        LD      A,L
        POP     HL
        LD      H,A
DIVSB:  LD      C,0FFH
DIVS1:  INC     C
        CALL    DIFF
        JP      NC,DIVS1
        ADD     HL,DE
        RET
;
DIFF:   LD      A,L
        SUB     E
        LD      L,A
        LD      A,H
        SBC     A,D
        LD      H,A
        RET
;
ABS:    LD      A,H             ;ABSOLUTE->HL SIGN->B
        OR      A
        RET     P
TWSCP:  LD      A,H             ;HL -HL
        CPL
        LD      H,A
        LD      A,L
        CPL
        LD      L,A
        INC     HL
        LD      A,B             ;SIGN EXCANGE
        XOR     80H
        LD      B,A
        RET
;
CMINT:  LD      A,H             ;SIGN CHECK
        XOR     D
        JP      P,CPIN1
        EX      DE,HL
CPIN1:  CALL    COMPR
        RET
;
LTSUB:  CALL    TSTV
        JP      C,LERMS
        PUSH    HL
;       TST     '''=''',LTERR
        CALL    TEST
        DB      '='
        DB      LTERR-$-1
        CALL    EEXPR
        LD      B,H
        LD      C,L
        POP     HL
        LD      (HL),C
        INC     HL
        LD      (HL),B
        RET
LTERR:  JP      LERMS
;
TSTSC:
;       TST     ''';''',TSCR1
        CALL    TEST
        DB      ';'
        DB      TSCR1-$-1
        POP     AF
        JP      NXTGO
;
TSCR1:
;       TST     0DH,TSRTN
        CALL    TEST
        DB      0DH
        DB      TSRTN-$-1
        POP     AF
        JP      RUN1
TSRTN:  RET
;
TSCR2:  CALL    SKPBL
        CP      0DH
        RET     Z
;
LERMS:  PUSH    DE              ;SAVE LINE POINTER
LEMS0:  LD      DE,WHTMS
LEMS1:  SUB     A
        CALL    MSG
        POP     DE              ;RECOVER LINE POINTER
        LD      A,(DE)
        PUSH    AF
        SUB     A
        LD      (DE),A
        LD      HL,(COBJ)
        PUSH    HL
        LD      A,(HL)
        INC     HL
        OR      (HL)
        POP     DE
        JP      Z,START
        LD      A,(HL)
        OR      A
        JP      M,ERRIN
        CALL    WLINE
        DEC     DE
        POP     AF
        LD      (DE),A
        LD      A,3FH           ;'?'
        CALL    PUT
        SUB     A
        CALL    MSG
        JP      START
;
SYSOF:  PUSH    DE
SYSO1:  LD      DE,SRYMS
        JP      LEMS1
;
GETL:   CALL    PUT
        LD      DE,LBUF
GETL1:  CALL    BREAK           ;GET CHARACTER
        JP      Z,GETL1
;       CALL    PUT
        CP      0AH
        JP      Z,GETL1         ;NEG. LF
        OR      A
        JP      Z,GETL1         ;NEG. NUL
        CP      7FH
        JP      Z,RUB           ;RUB?
        CP      7DH             ; ?
        JP      Z,CAN
        LD      (DE),A          ;STORE INTO LINE BUFFER
        INC     DE
        CP      0DH             ;CR?
        RET     Z
        LD      A,E             ;BUF. FULL?
        CP      7FH
        JP      NZ,GETL1
;
RUB:    LD      A,E             ;BUF EMPTY?
        CP      37H
        JP      Z,CAN
        DEC     DE
        LD      A,5CH           ;"\" PRINT
        CALL    PUT
        JP      GETL1
;
CAN:    CALL    CRLF            ;CANCEL LINE
        LD      A,5EH
        JP      GETL
;
SRCH:   LD      A,H             ;SEARCH CURRENT LINE POSI
        OR      A               ;HL=INPUT LABEL NO
        JP      M,ERROR
        LD      DE,OTOP
SRCH1:  PUSH    HL
        LD      HL,(OBTM)
        DEC     HL
        CALL    COMPR           ;COMPARE OBJ BOTTOM TO OB
        POP     HL
        RET     C
        LD      A,(DE)          ;COMPARE OBJ LABEL TO INP
        SUB     L
        LD      B,A
        INC     DE
        LD      A,(DE)
        SBC     A,H
        JP      C,SKPL1         ;IF < SKIP LINE
        DEC     DE
        OR      B
        RET                     ;RETURN GT(A<>0) EQ(A=0)
;
SKIPL:  INC     DE
SKPL1:  INC     DE
SKPL2:  LD      A,(DE)
        CP      0DH
        JP      NZ,SKPL1        ;SKIP UNTIL 'CR'
        INC     DE
        JP      SRCH1
;
MSG:    LD      B,A
MSG1:   LD      A,(DE)
        INC     DE
        CP      B
        RET     Z
        CALL    PUT
        CP      0DH
        JP      NZ,MSG1
        RET
;
PR10:
;       TST     '''"''',PR13
        CALL    TEST
        DB      '"'
        DB      PR13-$-1
        LD      A,22H
PR11:   CALL    MSG
        CP      0DH
        POP     HL
        JP      Z,RUN1
PR12:   INC     HL
        INC     HL
        INC     HL
        JP      (HL)
;
PR13:
;       TST     '''''''''',PR14
        CALL    TEST
        DB      "'"
        DB      PR14-$-1
        LD      A,27H
        JP      PR11
;
PR14:
;       TST     5FH,PR15
        CALL    TEST
        DB      5FH
        DB      PR15-$-1
        LD      A,8DH
        CALL    PUT
        CALL    PUT
        POP     HL
        JP      PR12
;
PR15:   RET
;
WINT:   PUSH    DE              ;WRITE INTEGER
        LD      DE,000AH
        PUSH    DE
        LD      B,D
        DEC     C
        CALL    ABS
        JP      P,WINT1
        LD      B,2DH           ;'-'
        DEC     C
WINT1:  PUSH    BC
WINT2:  CALL    DIVID
        LD      A,B
        OR      C
        JP      Z,WINT3
        EX      (SP),HL
        DEC     L
        PUSH    HL
        LD      H,B
        LD      L,C
        JP      WINT2
WINT3:  POP     BC
WINT4:  DEC     C
        LD      A,C
        OR      A
        JP      M,WINT5
        LD      A,' '
        CALL    PUT
        JP      WINT4
WINT5:  LD      A,B
        CALL    PUT
        LD      E,L
WINT6:  LD      A,E
        CP      0AH
        POP     DE
        RET     Z
        ADD     A,30H
        CALL    PUT
        JP      WINT6
;
WLINE:  LD      A,(DE)          ;HL (DE),(DE+1)
        LD      L,A
        INC     DE
        LD      A,(DE)
        LD      H,A
        INC     DE
        LD      C,04H           ;C=COLMN
        CALL    WINT
        LD      A,' '
        CALL    PUT
        SUB     A
        CALL    MSG
        RET
;
TRNSF:  CALL    COMPR           ;(BC)  (DE) (HL-1)
        RET     Z
        LD      A,(DE)
        LD      (BC),A
        INC     DE
        INC     BC
        JP      TRNSF
;
TR2:    LD      A,B             ;(BC+1) (DE)  (HL-1)
        SUB     D
        JP      NZ,TR2E
        LD      A,C
        SUB     E
        RET     Z               ;IF BC=DE RETURN
TR2E:   DEC     DE
        DEC     HL
        LD      A,(DE)
        LD      (HL),A
        JP      TR2
;
POPV:   POP     BC
        POP     HL
        LD      (FCNTR),HL
        LD      A,H
        OR      L
        JP      Z,NPOP
        POP     HL
        LD      (FSTPV),HL
        POP     HL
        LD      (FTOV),HL
        POP     HL
        LD      (FLABL),HL
        POP     HL
        LD      (FOBJ),HL
NPOP:   PUSH    BC
        RET
;
PSHV:   LD      HL,MSTK
        CALL    TWSCP
        POP     BC
        ADD     HL,SP
        JP      NC,SYSOF
        LD      HL,(FCNTR)
        LD      A,H
        OR      L
        JP      Z,NPSH
        LD      HL,(FOBJ)
        PUSH    HL
        LD      HL,(FLABL)
        PUSH    HL
        LD      HL,(FTOV)
        PUSH    HL
        LD      HL,(FSTPV)
        PUSH    HL
        LD      HL,(FCNTR)
NPSH:   PUSH    HL
        PUSH    BC
        RET
;
TTYO1:
TTYO0:
        POP     AF
        PUSH    AF
        CALL    PUTCH
        POP     AF
        CP      0DH
        RET     NZ
        LD      A,0AH
        CALL    PUT
        LD      A,0DH
        RET
;
BREAK:
        CALL    KBHIT
        OR      A
;       RET     NZ
        RET     Z
TTYI1:
        CALL    GETCH
        AND     7FH
        CP      13H
        JP      NZ,TTYI2
TTYI3:  CALL    GETCH
        CP      11H
        JP      NZ,TTYI3
        JP      BREAK
;
TTYI2:  CP      03H
        RET     NZ
        JP      START
;
MONBK:  CALL    EXIT
        RET
;
;
;
;

STRUP:  LD      HL,OTOP         ;START UP (NEW COMMAND)
        LD      (OBTM),HL
        LD      DE,STUPM
        CALL    MSG
        RET

STUPM   DB      'TINY BASIC FOR CP/M',0DH

EXIT:   LD      C,0             ;EXIT TO SYSTEM
        CALL    5
        RET


PUTCH:  PUSH    BC              ;OUTPUT 1 CHAR
        PUSH    DE
        LD      E,A
        LD      C,2
        CALL    5
        POP     DE
        POP     BC
        RET


KBHIT:  PUSH    BC              ;CHECK KEY STATUS
        LD      C,11
        CALL    5
        XOR     00H
        POP     BC
        RET


GETCH:  PUSH    BC              ;INPUT 1 CHAR
        LD      C,1
        CALL    5
        CP      0DH
        JP      NZ,NONL
        PUSH    AF
        LD      A,0AH
        CALL    PUT
        POP     AF
NONL:   POP     BC
        RET

ECODE:  EQU     $

        ORG     RAMST
COBJ    DS      0002H
RSTCK   DS      0002H
NCNTR   DS      0002H
FCNTR   DS      0002H
FSTPV   DS      0002H
FTOV    DS      0002H
FLABL   DS      0002H
FOBJ    DS      0002H
RWRK    DS      0002H
OBTM    DS      0002H
OTOP    DS      0001H
        END

元の東大版はインテル8080ニモニック表記なのは当然で、買うとお高いマクロアセンブラを使ってる。お安いXA80はマクロもリロケータブルも対応していなくてコメントとEQU定義とDB、DSくらいしか使えない。東大のお偉い教授サンとは使える環境が違ったんだよ。

広告

SONY ICF-M780N その2。

前回の分解では基板の表側は見なかったし、シールドも外さなかったので消化不良だったSONYのICF-M780N。

SONY ICF-M780N は DSPラジオ。
https://mzex.wordpress.com/2018/02/24/12466/

ICF-M780N_21

 

壊すかもしれないという不安よりどうなっているのかという技術的興味が上回ったのでさっそく分解。

ICF-M780N_22

基板表面。

「Destinction Table」「LCD Table」という表記が。それぞれCED/EA6/Jという項目があり、Destinction TableはJにマーキング。どう見ても「日本向け」設定のことだろう。CED、EA6が他の選択肢とすると、ICF-N780SLも地域違いで2バージョンあるのかも。

ICF-M780N_23

コントローラのシールドも外してみた。NEC製のD78F0451というコントローラっぽい。どこかにDestinction Tableを設定するところがあるのかと期待して手あたり次第ジャンパを飛ばしてみたが特に変化なし。あまりいじると本当に壊してしまうのでその前に終了。

ICF-M780N_24

「FLMDO」のシルクが非常に気になるが空きランドも無いし、コントローラは専用のASICではなくて汎用品のようだから、「TX」「RX」のランドが目立つところを考えても組み立てた後にオンボードで制御プログラムを書き込むようにしているのかもしれない。それならハンダゴテ一本でどういじったところで何も変わらないだろう。

日本人はソニーやパナソニックのラジオでラジオNIKKEI以外の短波放送を聴いてはいけないのだと理解した。今は戦前か。

ラジオのAM放送廃止を要請へ
https://www.47news.jp/3391134.html

今日のニュースにこんな記事が。そう遠くない将来、AM民放放送が無くなるかもしれないそうで。現状のFM補完放送って地方だと電波が弱くて正直AMの方が入りやすい。この状態のままでAM止めたら地方民はもっとラジオを聞かなくなるよ。それとAMの海外放送は聞きやすくなるが、FM-Highが増えたらこれからの季節の密かな楽しみEスポでの海外FM放送が聴きにくくなるかも。

 

Visual C++でUEFIアプリケーションを作ってみる。

link.exeの/?オプションの簡易ヘルプを見ていたら/MACHINE:{ARM|ARM64|EBC|X64|X86}の選択肢EBCが気になった。

EBCはEFI Byte Codeのことで、今どきのPC/AT互換機に備わっている旧来のBIOSを置き換えるUEFIと呼ばれるファームウェア上で動かせるプログラムのことらしい。インテル的には2020年にはMBRをロードするBIOSは無くしたいらしくピュアなUEFIに一本化するみたい。個人的にはDOS互換が完全に消え失せたらPC/AT互換機ではないしDOS/V機でも無くなるっていうのが残念で仕方ないが、今使ってるWindows 10もUEFIブートマシンだからたぶん困ることも無さそう。

前に調べた通りUEFIはストレージのFATファイルシステムを解釈できてファイル単位でデータを読み込んでOSを起動できる。その仕組みを組むのがEBCでできたEFIアプリケーションってことなので、EFIアプリを自分で作れるってことは、OSを起動する前にコンピュータにあれこれ指示できるっていうことになる。これはおもしろそう。EFIアプリ自体はWindowsのアプリExe形式と同じでPortable Executable形式になっていて、EDKⅡgnu-efiという開発ツールを利用すると良いことになっている。ネット上にはgnu-efiとgccを使う例はかなりたくさん出ているし、EDK2+Visual Studioの例も結構出ててそれらの手順でやればほぼ問題なく作成できるのだが、フルでEDKをインストール・セットアップしてVSでごりごりやりましょうというものか、フルのスクラッチでSDKすら使わないよとか、GNU/Linuxのgccでgnu-efiを使うのがジョーシキっしょ、みたいのが多くて自分的にはもうちょっとあっさりした環境でやりたかったからちょっと縛りを入れて、要件は次の通りとした。

・Visual C++ (2013 Professioanl)のコマンドラインコンパイラとリンカのみを使う。
・EDK2も含めて追加のライブラリ、ユーティリティは使わない。
・各種定義を無から書くのは面倒なので、EDK2のヘッダを拝借する。

1.EDK2をダウンロードして、適当なフォルダに展開。

2.VC++のコマンドラインコンパイラ(64ビット環境)が使える「CL環境」にEDK2のインクルードパスに2か所追加する。

set include=%include%;C:\edk2\MdePkg\Include;C:\edk2\MdePkg\Include\X64

3.コードを書く。

※Wordpressの表示でおかしくなるので半角の<>は全角の<>に置換。

[EfiHello1.cpp]

// EDKⅡのヘッダファイルを流用
extern "C" {
#pragma warning(disable:4804)
#include <Uefi.h>
}

// EFIアプリケーション エントリーポイント
EFI_STATUS EFIAPI EfiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
	
	// 初期値5分で発動するウオッチドッグタイマーを無効にする。
	SystemTable->BootServices->SetWatchdogTimer(0, 0, 0, (CHAR16*)NULL);
	
	// 画面クリアとメッセージ出力
	SystemTable->ConOut->ClearScreen(SystemTable->ConOut);
	SystemTable->ConOut->OutputString(SystemTable->ConOut, (CHAR16*)L"Hello World!\r\n");

	// キー入力とエコーバックを行う
	// 'q'でプログラム終了
	EFI_INPUT_KEY key;		// キー入力受け取り用
	wchar_t buf[2] = L" ";	// 表示用バッファ
	while(TRUE) {
		// キー入力
		if (!SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn, &key)) {
			// エコーバック
			buf[0] = key.UnicodeChar;
			SystemTable->ConOut->OutputString(SystemTable->ConOut, (CHAR16*)buf);
			
			// 入力に応じて挙動を変える
			if(key.UnicodeChar == L'q') {	// 'q'だったら終了
				break;
			}
			else if(key.UnicodeChar == L'\r') {	// リターンだったら改行も表示する
				buf[0] = L'\n';
				SystemTable->ConOut->OutputString(SystemTable->ConOut, (CHAR16*)buf);
			}
		}
	}

	return EFI_SUCCESS;
}

*SystemTableが超重要。アプリ側に渡されるもので、これを通してEFIのファンクションを呼び出す。オブジェクト指向っぽい感じがするが、C++のVtableよりもMSのCOMっぽい。

4.コンパイル

cl /GS- EfiHello1.cpp /link /out:bootx64.efi /subsystem:efi_application /entry:EfiMain

/GS- はバッファのセキュリティチェックを無効にする設定。セキュリティチェックに別途ライブラリ関数を呼び出すらしくUEFIアプリ作成時は使えないので無効にする。
/subsystem:efi_applicationが重要。
/entry:EfiMainはエントリポイントを指定する。CRT0なんかのスタートアップルーチンは無いのでソースに合わせれば何でも良い。

5.実行してみる。
5-1.出来上がったbootx64.efiをUSBメモリの/EFI/BOOT/にコピー。次の場所にファイルが存在するように。
(USB:)/EFI/BOOT/bootx64.efi

5-2.UEFIブートができるPCにUSBメモリを差し込んで起動。起動時オプションでUSBメモリから起動してみる。

QEMU+OVMFで動かしてみた。もちろん実機でも動く。
uefihello.png

お決まりのHello World!を表示したらあとはキー入力をエコーバックする。飽きたら電源を切る。一応qでアプリ終了。

本当にCだけ(本当はC++)で書いたプログラムがOS起動前のPCで動作する、というのは結構感動的。DOSをはじめMBRブートが使えなくなるのは残念だけど、UEFIは非常にすっきりしていて潔い。