OpenCores
URL https://opencores.org/ocsvn/rtf68ksys/rtf68ksys/trunk

Subversion Repositories rtf68ksys

[/] [rtf68ksys/] [trunk/] [Software/] [BOOTROM.x68] - Rev 4

Compare with Previous | Blame | View Log

;------------------------------------------------------------------------------
; This is a bit of a mess.
;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
; 0x00000 to 0x00007    boot vector
; 0x00008 to 0x003ff    interrupt vectors
; 0x00400 to 0x                 system variables
; 0x10000 to 0x1ffff    thread control blocks
; 0x20000 to 0x3ffff    bitmap memory, page 1
; 0x40000 to 0x5ffff    bitmap memory, page 2
; 0x60000 to 0x7ffff    initial thread stacks
;------------------------------------------------------------------------------
INACTIVE        EQU             0x0000
ACTIVE          EQU             0x0001

CR      EQU     0x0D            ;ASCII equates
LF      EQU     0x0A
TAB     EQU     0x09
CTRLC   EQU     0x03
CTRLH   EQU     0x08
CTRLS   EQU     0x13
CTRLX   EQU     0x18

Milliseconds    EQU             0x400
Lastloc                 EQU             0x404
ScreenPtr       EQU             0x410
ScreenColor     EQU             0x414
CursorRow       EQU             0x418
CursorCol       EQU             0x41A
KeybdEcho       EQU             0x41C
PenColor        EQU             0x420
PenColor8       EQU             0x424
FillColor       EQU             0x428
FillColor8      EQU             0x42C
DrawPos         EQU             0x430
KeybdBuffer     EQU             0x440
KeybdHead       EQU             0x450
KeybdTail       EQU             0x452
Keybuf          EQU             0x460
memend          EQU             0x500
scratch1        EQU             0x700
S19StartAddress EQU             0x800
KEYBD           EQU             0xFFDC0000
TEXTSCR         EQU             0xFFD00000
COLORSCR        EQU             0xFFD10000
PSG                     EQU             0xFFD40000
SPRITERAM       EQU             0xFFD80000
PSG_FREQUENCY0  EQU             0xFFD40000
PSG_PULSEWIDTH0 EQU             0xFFD40002
PSG_CTRL0               EQU             0xFFD40004
PSG_ATTACK0             EQU             0xFFD40008
PSG_DECAY0              EQU             0xFFD4000A
PSG_SUSTAIN0    EQU             0xFFD4000C
PSG_RELEASE0    EQU             0xFFD4000E
PSG_MASTVOL             EQU             0xFFD40040
BITMAPSCR       EQU             0x00020000
UART            EQU             0xFFDC0A00
UART_LS         EQU             UART+1
UART_CTRL       EQU             UART+7
RANDOM          EQU             0xFFDC0C00
TEXTCTRL        EQU             0xFFDA0000
TEXT_COLS       EQU             0xFFDA0000
TEXT_ROWS       EQU             0xFFDA0002
TEXT_CURPOS     EQU             0xFFDA0016
STACK           EQU             0xFFFE07FC
TMPPMOD         EQU             0xFFDC0300
GRAPHICS        EQU             0xFFDAE000
G_DRAWLINE      EQU             0x0002

                CODE
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
; Clear all memory

; RAM test
        even

; We clear the screen to give a visual indication that the system
; is working at all.
;
        org     0xFFFF1100
main:
        move.w  #0xCE,ScreenColor       ; blue on blue
        move.b  #1,KeybdEcho            ; turn on keyboard echo
        jsr             ClearScreen
        clr.w   CursorRow
        clr.w   CursorCol
        lea             MSGRAM,a1
        jsr             DisplayString

    lea         main5,a3      ; get return address for ram test
    jmp         ramtest       ; Call ram test routine. (Called this way in case there's no RAM).
main5:
        ; setup user stack pointer
        ;
        move.l  ENDMEM,a0
        move.l  a0,usp

        ; reset the screen stuff
        ;       
        move.w  #0xCE,ScreenColor       ; blue on blue
        move.b  #1,KeybdEcho            ; turn on keyboard echo
        clr.w   CursorRow               ; reset after RAMTEST
        clr.w   CursorCol

        ; randomize sprite memory
        move.l  #32768,d1
        lea             SPRITERAM,a0
main6:
        move.l  RANDOM,d0
        move.w  d0,(a0)+
        subi.l  #1,d1
        bne             main6
        
        ; setup vector table
        ;
        lea             BusError,a0
        move.l  a0,0x008                ; set bus error vector
        lea             AddressError,a0
        move.l  a0,0x00C                ; set address error vector
        lea             IllegalInstruction,a0
        move.l  a0,0x010
        lea             Pulse1000,a0
        move.l  a0,0x078                ; set autovector 6
        lea             KeybdNMI,a0
        move.l  a0,0x07C                ; set autovector 7
        lea             0xFFFF0800,a0   
        move.l  a0,0x080                ; trap #0 AOS entry     - task switch
        lea             0xFFFF0400,a0   
        move.l  a0,0x084                ; trap #1 AOS entry - AOS system call dispatcher
        lea             0xFFFF0C00,a0   
        move.l  a0,0x088                ; trap #2 AOS entry - dispatcher
        lea             TRAP15,a0
        move.l  a0,0x0BC                ; set trap 15 vector

        clr.l   Milliseconds
        andi    #0xF000,sr              ; enable interrupts, stay in supervisor mode

        moveq   #14,d0
        lea             MSGBOOTING,a1   ; Display the boot message
        trap    #15

;       jsr             Beep

        ; test keyboard, wait for 'x' to be pressed
j7:
;       jmp             0xFFFF0000              ; start AOS
        jmp             START

        moveq   #5,d0
        trap    #15
        cmpi.b  #'x',d1
        bne             j7

        ; Clear bitmap memory
        move.l  #0x40000,d0
        lea             BITMAPSCR,a0
        move.w  #0x1234,d2
j8:
        move.w  d2,(a0)+
        subi.l  #1,d0                   ; can't use dbeq (>count ffff)
        bne             j8

        jmp             START                   ; goto tiny basic


        clr.b   UART_CTRL               ; turn off hardware flow control
j10:
        lea             0xFFFF0000,a2   ; start of bootstrap ROM
j9:
        move.b  (a2)+,d1
j6:     
        move.b  UART_LS,d0              ; check line status
        btst    #5,d0                   ; can we transmit more ?
        beq             j6                              ; no, go back
        move.b  d1,UART
        cmpa.l  #0xFFFF0100,a2
        blo             j9
        bra             j10
j2:
        move.b  0xFFDD0000,d0
        bpl             j2
        move.l  0xFFDD0004,a0
        jmp             (a0)

MSGRAM:
        dc.b    "RAM TEST",0
MSGBOOTING:
        dc.b    "BOOTING....",0
        
        align   16

;------------------------------------------------------------------------------
; Pressing Ctl-Alt-Del on the keyboard causes a keyboard NMI, the highest
; priority interrupt in the system. This should be almost the same as a reset.
;------------------------------------------------------------------------------
;
KeybdNMI:
        jmp             main
        rte

;------------------------------------------------------------------------------
; Unimplemented yet.
;
; Normal keyboard interrupt, the lowest priority interrupt in the system.
; Grab the character from the keyboard device and store it in a buffer.
;------------------------------------------------------------------------------
;
KeybdIRQ:
        movem.l a0/d0/d1,-(a7)
        move.w  KeybdHead,d1
        andi.w  #0xf,d1                                 ; D1 = index into buffer
        lea             KeybdBuffer,a0
KeybdIRQa:
        move.w  KEYBD,d0                                ; get keyboard character
        clr.w   KEYBD+2                                 ; clear keyboard strobe
        move.b  d0,(a0,d1.w)                    ; store character in buffer
        addi.w  #1,d1                                   ; increment head index
        andi.w  #0xF,d1
        move.w  d1,KeybdHead
KeybdIRQb:
        cmp.w   KeybdTail,d1                    ; check to see if we've collided
        bne             KeybdIRQc                               ; with the tail
        addi.w  #1,d1                                   ; if so, increment the tail index
        andi.w  #0xf,d1                                 ; the oldest character will be lost
        move.w  d1,KeybdTail
KeybdIRQc:
        movem.l (a7)+,a0/d0/d1
        rte

;------------------------------------------------------------------------------
; 1000 Hz interrupt
; - takes care of "flashing" the cursor
;------------------------------------------------------------------------------
;
Pulse1000:
        move.l  d0,-(a7)
        add.l   #1,Milliseconds
        add.w   #1,TEXTSCR+102
        tst.b   0xFFFF0000              ; clear interrupt
        move.l  Milliseconds,d0
        andi.b  #0x7f,d0
        cmpi.b  #64,d0
        bne             p10001
        bsr             FlashCursor
p10001:
        move.l  (a7)+,d0
        rte

;------------------------------------------------------------------------------
; Flash Cursor
;------------------------------------------------------------------------------
;
FlashCursor:
        movem.l a0/a1/d0/d2,-(a7)
        bsr             CalcScreenLoc
        adda.l  #0x10000,a0
        ; causes screen colors to flip around
        move.w  (a0),d0         
        ror.b   #4,d0
        move.w  d0,(a0)
        cmpa.l  Lastloc,a0
        beq             flshcrsr1
        ; restore the screen colors of the previous cursor location
        move.l  Lastloc,a1
        move.w  ScreenColor,(a1)
        move.l  a0,Lastloc
flshcrsr1:
        movem.l (a7)+,a0/a1/d0/d2
        rts

;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
IdleTask:
        LINK    A5,#-PSIZ       ;RESERVE SPACE ON STACK FOR INPUT COMMAND LINE;
    lea.l   -MLFLAG(A5),A0
        LEA.L   -STATZ(A5),A1
        MOVE.W  #EMPTY,-MLFLAG(A5)
        MOVE.W  #LETTE,-MXLTRS(A5)      ;SETUP FOR "LETTE" LETTERS (WAS 2);
        MOVE.L  #AOS_POSTBOX,D0
        TRAP    #1                      ;POST MAILBOX;
IdleTask1:
        add.w   #1,TEXTSCR+100
        trap    #0
        bra             IdleTask1

;------------------------------------------------------------------------------
; TRAP #15 handler
;------------------------------------------------------------------------------
;
TRAP15:
        movem.l d0/a0,-(a7)
        lea             T15DispatchTable,a0
        andi.l  #0x0ff,d0
        asl.l   #2,d0
        move.l  (a0,d0.w),a0
        jsr             (a0)
        movem.l (a7)+,d0/a0
        rte

T15DispatchTable:
; Task 0
dc.l    DisplayString0
dc.l    DisplayString1
dc.l    StubRout
dc.l    DisplayNum3
dc.l    StubRout
dc.l    GetKey
dc.l    DisplayChar
dc.l    CheckForKey
dc.l    StubRout
dc.l    StubRout
; Task 10
dc.l    StubRout
dc.l    Cursor1
dc.l    SetKeyboardEcho
dc.l    DisplayStringCRLF
dc.l    DisplayString
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
; Task 20
dc.l    DisplayNum20
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
; Task 30
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
; Task 40
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
; Task 50
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
; Task 60
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
; Task 70
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
dc.l    StubRout
; Task 80
dc.l    SetPenColor
dc.l    SetFillColor
dc.l    DrawPixel
dc.l    StubRout
dc.l    DrawLine
dc.l    DrawLineTo
dc.l    MoveTo
dc.l    FillRectangle
dc.l    StubRout
dc.l    StubRout
; Task 90
dc.l    DrawRectangle

;------------------------------------------------------------------------------
; Stub routine for unimplemented functionality.
;------------------------------------------------------------------------------
;
StubRout:
        rts

;------------------------------------------------------------------------------
; Set the graphics mode pen color
;------------------------------------------------------------------------------
SetPenColor:
        movem.l d0/d1,-(a7)
        move.l  d1,PenColor
        bsr             Cvt24To8
        move.b  d1,PenColor8
        movem.l (a7)+,d0/d1
        rts

SetFillColor:
        movem.l d0/d1,-(a7)
        move.l  d1,FillColor
        bsr             Cvt24To8
        move.b  d1,FillColor8
        movem.l (a7)+,d0/d1
        rts

Cvt24To8:
        movem.l d0/d2,-(a7)
        clr.l   d2
        ror.l   #6,d1
        move.l  d1,d0
        and.b   #3,d0
        move.b  d0,d2
        ror.l   #2,d1                   ;
        ror.l   #5,d1
        move.b  d1,d0
        and.b   #7,d0
        asl.w   #2,d0
        or.b    d0,d2
        ror.l   #3,d1
        ror.l   #5,d1
        move.b  d1,d0
        and.b   #7,d0
        asl.w   #5,d0
        or.b    d0,d2
        move.l  d2,d1
        movem.l (a7)+,d0/d2
        rts

;------------------------------------------------------------------------------
; d1.w = X
; d2.w = Y
;------------------------------------------------------------------------------
DrawPixel:
        movem.l a0/d1/d2,-(a7)
        mulu.w  #208,d2         ; Y * 208
        andi.l  #0xffff,d2
        asl.l   #1,d2           ; Y * 416
        and.l   #0x1ff,d1
        add.l   d1,d2           ; Y * 416 + X
        add.l   #BITMAPSCR,d2
        move.l  d2,a0
        move.b  PenColor8,(a0)
        movem.l (a7)+,a0/d1/d2
        rts
        
;------------------------------------------------------------------------------
; d1.w = X1
; d2.w = Y1
; d3.w = X2
; d4.w = Y2
;
; From Wikipedia:
;
;function line(x0, y0, x1, y1)
;   dx := abs(x1-x0)
;   dy := abs(y1-y0) 
;   if x0 < x1 then sx := 1 else sx := -1
;   if y0 < y1 then sy := 1 else sy := -1
;   err := dx-dy
; 
;   loop
;     setPixel(x0,y0)
;     if x0 = x1 and y0 = y1 exit loop
;     e2 := 2*err
;     if e2 > -dy then 
;       err := err - dy
;       x0 := x0 + sx
;     end if
;     if e2 <  dx then 
;       err := err + dx
;       y0 := y0 + sy 
;     end if
;   end loop
;
; Registers
; d1,d2,d3,d4 = X1,Y1,X2,Y2 respectively
; d0 = sx, d7 = sy
; d5 = dx, d6 = dy
; a2 = err
; a3 = 2*err
;------------------------------------------------------------------------------
DrawLine:
        movem.l d0/d1/d2/d3/d4/d5/d6/d7/a2/a3,-(a7)
        andi.l  #0x1ff,d1
        andi.l  #0x1ff,d2
        andi.l  #0x1ff,d3
        andi.l  #0x1ff,d4
        move.w  d3,DrawPos              ; X
        move.w  d4,DrawPos+2    ; Y
        move.l  d1,d5
        sub.l   d3,d5
        bpl             dl1
        neg.l   d5              ; d5 = dx
dl1:
        move.l  d2,d6
        sub.l   d4,d6
        bpl             dl2
        neg.l   d6              ; d6 = dy
dl2:
        moveq   #1,d0   ; sx = 1
        moveq   #1,d7   ; sy = 1
        cmp.l   d3,d1
        blo             dl3
        neg.l   d0              ; sx = -1
dl3:
        cmp.l   d4,d2
        blo             dl5
        neg.l   d7              ; sy = -1
dl5:
        move.l  d5,a2
        suba.l  d6,a2   ; err = dx-dy
        neg.l   d6                      ; -dy

DrawLineLoop:
        bsr             DrawPixel
        cmp.l   d1,d3           ; x0 = x1 ?
        bne             dl7                     ; no, keep going
        cmp.l   d2,d4           ; y0 = y1 ?
        beq             dldone          ; yes -> line draw is done
dl7:
        move.l  a2,a3           ; e2 = err
        adda.l  a3,a3           ; e2 = 2*err    
        cmpa.l  d6,a3           ; if (e2 > -dy)
        ble             dl8
        adda.l  d6,a2           ;     err = err + -dy
        add.l   d0,d1           ;     x0 = x0 + sx
dl8:
        cmpa.l  d5,a3           ; if (e2 < dx)
        bge             dl9
        adda.l  d5,a2           ;     err = err + dx
        add.l   d7,d2           ;     y0 = y0 + sy
dl9:
        bra DrawLineLoop
dldone:
        movem.l (a7)+,d0/d1/d2/d3/d4/d5/d6/d7/a2/a3
        rts

;------------------------------------------------------------------------------
; Draw line to X,Y
; D1.W = X
; D2.W = Y
;------------------------------------------------------------------------------
;
DrawLineTo:
        movem.l d1/d2/d3/d4,-(a7)
        move.w  d1,d3
        move.w  d2,d4
        move.w  DrawPos,d1
        move.w  DrawPos+2,d2
        bsr             DrawLine
        movem.l (a7)+,d1/d2/d3/d4
        rts


;------------------------------------------------------------------------------
; Move drawing position to X,Y
; d1.w = X
; d2.w = y
;------------------------------------------------------------------------------
;
MoveTo:
        move.w  d1,DrawPos
        move.w  d2,DrawPos+2
        rts

DrawRectangle:
        movem.l d0/d1/d2/d3/d4/d5/d6/d7,-(a7)
        move.w  d1,d0
        move.w  d2,d7
        move.w  d3,d5
        move.w  d4,d6
        move.w  d2,d4
        bsr             DrawLine
        move.w  d3,d1
        move.w  d4,d2
        move.w  d5,d3
        move.w  d6,d4
        bsr             DrawLine
        move.w  d3,d1
        move.w  d4,d2
        move.w  d0,d3
        move.w  d6,d4
        bsr             DrawLine
        move.w  d3,d1
        move.w  d4,d2
        move.w  d0,d3
        move.w  d7,d4
        bsr             DrawLine
        movem.l (a7)+,d0/d1/d2/d3/d4/d5/d6/d7
        rts
        
;------------------------------------------------------------------------------
; Draw a filled rectangle
;------------------------------------------------------------------------------
FillRectangle:
        movem.l d1/d2/d3/d4,-(a7)
        move.w  PenColor8,-(a7)
        bsr     DrawRectangle
        move.w  FillColor8,PenColor8
FillRect3:
        cmp.w   d1,d3
        blo             FillRect1
        cmp.w   d2,d4
        bhs             FillRect2
FillRect1:
        addi.w  #1,d1
        addi.w  #1,d2
        subi.w  #1,d3
        subi.w  #1,d4
        bsr             DrawRectangle
        bra             FillRect3
FillRect2:
        move.w  (a7)+,PenColor8
        movem.l (a7)+,d1/d2/d3/d4
        rts

;------------------------------------------------------------------------------
; d1.b 0=echo off, non-zero = echo on
;------------------------------------------------------------------------------
SetKeyboardEcho:
        move.b  d1,KeybdEcho
        rts

;------------------------------------------------------------------------------
; read ascii character into d1.b
;------------------------------------------------------------------------------
;
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        
GetKey:
        move.w  KEYBD,d1
        bpl     GetKey
        clr.w   KEYBD+2                 ; clear the keyboard strobe
        and.w   #0xFF,d1                ; remove strobe bit
        cmpi.b  #0,KeybdEcho    ; is keyboard echo on ?
        beq             gk1
        cmpi.b  #'\r',d1                ; convert CR keystroke into CRLF
        beq             CRLF
        jsr             DisplayChar
gk1:
        rts

;------------------------------------------------------------------------------
; get key pending status into d1.b
;------------------------------------------------------------------------------
;
CheckForKey:
        move.w  KEYBD,d1
        bpl             cfk1
        move.b  #1,d1
        rts
cfk1:
        clr.b   d1
        rts

;
CRLF:
        move.l  d1,-(a7)
        move.b  #'\r',d1
        jsr             DisplayChar
        move.b  #'\n',d1
        jsr             DisplayChar
        move.l  (a7)+,d1
        rts

;------------------------------------------------------------------------------
; Calculate screen memory location from CursorRow,CursorCol.
; Destroys d0,d2,a0
;------------------------------------------------------------------------------
;
CalcScreenLoc:
        move.w  CursorRow,d0            ; compute screen location
        andi.w  #0x7f,d0
        mulu.w  TEXT_COLS,d0
        move.w  CursorCol,d2
        andi.w  #0xff,d2
        add.w   d2,d0
        asl.w   #1,d0
        add.l   #TEXTSCR,d0
        move.l  d0,a0                           ; a0 = screen location
        lsr.l   #1,d0
        move.w  d0,TEXT_CURPOS
        rts

;------------------------------------------------------------------------------
; Display a character on the screen
; d1.b = char to display
;------------------------------------------------------------------------------
;
DisplayChar:
        cmpi.b  #'\r',d1                        ; carriage return ?
        bne             dccr
        clr.w   CursorCol                       ; just set cursor column to zero on a CR
        rts
dccr:
        cmpi.b  #0x91,d1                        ; cursor right ?
        bne     dcx6
        cmpi.w  #51,CursorCol
        beq             dcx7
        addi.w  #1,CursorCol
dcx7:
        rts
dcx6:
        cmpi.b  #0x90,d1                        ; cursor up ?
        bne             dcx8
        cmpi.w  #0,CursorRow
        beq             dcx7
        subi.w  #1,CursorRow
        rts
dcx8:
        cmpi.b  #0x93,d1                        ; cursor left?
        bne             dcx9
        cmpi.w  #0,CursorCol
        beq             dcx7
        subi.w  #1,CursorCol
        rts
dcx9:
        cmpi.b  #0x92,d1                        ; cursor down ?
        bne             dcx10
        cmpi.w  #30,CursorRow
        beq             dcx7
        addi.w  #1,CursorRow
        rts
dcx10:
        cmpi.b  #0x94,d1                        ; cursor home ?
        bne             dcx11
        cmpi.w  #0,CursorCol
        beq             dcx12
        clr.w   CursorCol
        rts
dcx12
        clr.w   CursorRow
        rts
dcx11:
        movem.l d0/d1/d2/a0,-(a7)
        cmpi.b  #0x99,d1                        ; delete ?
        bne             dcx13
        bsr             CalcScreenLoc
        move.w  CursorCol,d0
        bra             dcx5
dcx13:
        cmpi.b  #CTRLH,d1                       ; backspace ?
        bne     dcx3
        cmpi.w  #0,CursorCol
        beq     dcx4
        subi.w  #1,CursorCol
        bsr             CalcScreenLoc           ; a0 = screen location
        move.w  CursorCol,d0
dcx5:
        move.w  2(a0),(a0)+
        addi.w  #1,d0
        cmp.w   TEXT_COLS,d0
        blo             dcx5
        move.w  #32,d0
        move.w  d0,-2(a0)
        bra             dcx4
dcx3:
        cmpi.b  #'\n',d1                ; linefeed ?
        beq             dclf

        bsr             CalcScreenLoc   ; a0 = screen location
        bsr             AsciiToScreen   ; convert ascii char to screen char
        move.w  d1,(a0)+
        bsr             IncCursorPos
        movem.l (a7)+,d0/d1/d2/a0
        rts
dclf:
        bsr             IncCursorRow
dcx4:
        movem.l (a7)+,d0/d1/d2/a0               ; get back a0
        rts

;------------------------------------------------------------------------------
; Increment the cursor position, scroll the screen if needed.
;------------------------------------------------------------------------------
;
IncCursorPos:
        addi.w  #1,TEXT_CURPOS
        addi.w  #1,CursorCol
        move.w  TEXT_COLS,d0
        cmp.w   CursorCol,d0
        bhs             icc1
        clr.w   CursorCol
IncCursorRow:
        addi.w  #1,CursorRow
        move.w  TEXT_ROWS,d0
        cmp.w   CursorRow,d0
        bhi             icc1
        move.w  TEXT_ROWS,d0
        move.w  d0,CursorRow            ; in case CursorRow is way over
        subi.w  #1,CursorRow
        asl.w   #1,d0
        sub.w   d0,TEXT_CURPOS
        bsr             ScrollUp
icc1:
        rts

;------------------------------------------------------------------------------
; Display a string on the screen.
;------------------------------------------------------------------------------
;
DisplayString:
        movem.l d0/d1/a1,-(a7)
dspj1:
        clr.l   d1                              ; clear upper bits of d1
        move.b  (a1)+,d1                ; move string char into d1
        cmpi.b  #0,d1                   ; is it end of string ?
        beq             dsret                   
        bsr             DisplayChar             ; display character
        bra             dspj1                   ; go back for next character
dsret:
        movem.l (a7)+,d0/d1/a1
        rts

DisplayStringCRLF:
        bsr             DisplayString
        bra             CRLF

;------------------------------------------------------------------------------
; Display a string on the screen. Stop at 255 chars, or NULL or D1.W
;------------------------------------------------------------------------------
;
DisplayString1:
        movem.l d0/d1/a1,-(a7)
        andi.w  #255,d1                 ; max 255 chars
        move.l  d1,d0
dspj11:
        move.b  (a1)+,d1                ; move string char into d1
        cmpi.b  #0,d1                   ; is it end of string ?
        beq             dsret1                  
        bsr             DisplayChar             ; display character
        dbeq    d0,dspj11               ; go back for next character
dsret1:
        movem.l (a7)+,d0/d1/a1
        rts

;------------------------------------------------------------------------------
; Display a string on the screen. Stop at 255 chars, or NULL or D1.W
; end string with CR,LF
;------------------------------------------------------------------------------
;
DisplayString0:
        bsr             DisplayString1
        bra             CRLF

;------------------------------------------------------------------------------
; Dispatch cursor functions
;------------------------------------------------------------------------------
;
Cursor1:
        cmpi.w  #0x00ff,d1
        beq             GetCursorPos
        cmpi.w  #0xFF00,d1
        beq             SetCursorPos
        jsr             ClearScreen
        rts

;------------------------------------------------------------------------------
; Get the cursor position.
; d1.b0 = row
; d1.b1 = col
;------------------------------------------------------------------------------
;
GetCursorPos:
        move.w  CursorCol,d1
        asl.w   #8,D1
        move.b  CursorRow,d1
        rts

;------------------------------------------------------------------------------
; Set the position of the cursor, update the linear screen pointer.
; d1.b0 = row
; d1.b1 = col
;------------------------------------------------------------------------------
;
SetCursorPos:
        move.l  d1,-(a7)
        move.b  d1,CursorRow
        lsr.w   #8,d1
        move.w  d1,CursorCol
        move.w  CursorRow,d1
        mulu.w  TEXT_COLS,d1
        add.w   CursorCol,d1
        asl.w   #1,d1
        move.w  d1,TEXT_CURPOS
scp1:
        move.l  (a7)+,d1
        rts

;------------------------------------------------------------------------------
; Clear the screen and the screen color memory
; We clear the screen to give a visual indication that the system
; is working at all.
;------------------------------------------------------------------------------
;
ClearScreen:
        move.w  TEXT_COLS,d1    ; calc number to clear
        mulu.w  TEXT_ROWS,d1
        move.w  #32,d0                  ; space character
        move.l  #TEXTSCR,a0             ; text screen address
csj4:
        move.w  d0,(a0)+
        dbeq    d1,csj4

        move.w  TEXT_COLS,d1    ; calc number to clear
        mulu.w  TEXT_ROWS,d1
        move.w  ScreenColor,d0          ; a nice color blue, light blue
        move.l  #COLORSCR,a0            ; text color address
csj3:
        move.w  d0,(a0)+
        dbeq    d1,csj3
        rts
        
;------------------------------------------------------------------------------
; Scroll text on the screen upwards
;------------------------------------------------------------------------------
;
ScrollUp:
        movem.l d0/d1/d2/a0,-(a7)
        move.w  TEXT_COLS,d0            ; calc number of chars to scroll
        mulu.w  TEXT_ROWS,d0
        sub.w   TEXT_COLS,d0            ; one less row
        lea             TEXTSCR,a0
        move.w  TEXT_COLS,d2
        asl.w   #1,d2
scrup1:
        move.w  (a0,d2.w),(a0)+
        dbeq    d0,scrup1

        move.w  TEXT_ROWS,d1
        subi.w  #1,d1
        jsr             BlankLine
        movem.l (a7)+,d0/d1/d2/a0
        rts

;------------------------------------------------------------------------------
; Blank out a line on the display
; line number to blank is in D1.W
;------------------------------------------------------------------------------
;
BlankLine:
        movem.l d0/a0,-(a7)
        move.w  TEXT_COLS,d0
        mulu.w  d1,d0                           ; d0 = row * cols
        asl.w   #1,d0                           ; *2 for moving words, not bytes
        add.l   #TEXTSCR,d0                     ; add in screen base
        move.l  d0,a0
        move.w  TEXT_COLS,d0            ; d0 = number of chars to blank out
blnkln1:
        move.w  #' ',(a0)+
        dbeq    d0,blnkln1
        movem.l (a7)+,d0/a0
        rts     

;------------------------------------------------------------------------------
; d1 = number
; d2.b = column width
;------------------------------------------------------------------------------
DisplayNum20:
        movem.l d0/d1/d2/d3/d4,-(a7)
        clr.l   d4
        move.b  d2,d4
        jsr             PRTNUM
        movem.l (a7)+,d0/d1/d2/d3/d4
        rts
        
;------------------------------------------------------------------------------
; d1 = number
;------------------------------------------------------------------------------
DisplayNum3:
        movem.l d0/d1/d2/d3/d4,-(a7)
        clr.l   d4
        jsr             PRTNUM
        movem.l (a7)+,d0/d1/d2/d3/d4
        rts

;------------------------------------------------------------------------------
; Convert ASCII character to screen display character.
;------------------------------------------------------------------------------
;
AsciiToScreen:
        andi.w  #0x00ff,d1
        cmpi.b  #'A',d1
        blo             atoscr1
        cmpi.b  #'Z',d1
        bls             atoscr1
        cmpi.b  #'z',d1
        bhi     atoscr1
        cmpi.b  #'a',d1
        blo     atoscr1
        subi.b  #0x60,d1
atoscr1:
        ori.w   #0x100,d1
        rts
        
;------------------------------------------------------------------------------
; Convert screen character to ascii character
;------------------------------------------------------------------------------
;
ScreenToAscii:
        andi.b  #0xff,d1
        cmpi.b  #26,d1
        bhi             stasc1
        addi.b  #0x60,d1
stasc1:
        rts

;------------------------------------------------------------------------------
; Display nybble in D1.B
;------------------------------------------------------------------------------
;
DisplayNybble:
        move.w  d1,-(a7)
        andi.b  #0xF,d1
        addi.b  #'0',d1
        cmpi.b  #'9',d1
        bls             dispnyb1
        addi.b  #7,d1
dispnyb1:
        bsr             DisplayChar
        move.w  (a7)+,d1
        rts

;------------------------------------------------------------------------------
; Display the byte in D1.B
;------------------------------------------------------------------------------
;
DisplayByte:
        move.w  d1,-(a7)
        ror.b   #4,d1
        bsr             DisplayNybble
        rol.b   #4,d1
        bsr             DisplayNybble
        move.w  (a7)+,d1
        rts

;------------------------------------------------------------------------------
; Display the 32 bit word in D1.L
;------------------------------------------------------------------------------
;
DisplayWord:
        rol.l   #8,d1
        bsr             DisplayByte
        rol.l   #8,d1
        bsr             DisplayByte
        rol.l   #8,d1
        bsr             DisplayByte
        rol.l   #8,d1
        bsr             DisplayByte
        rts

DisplayMem:
        move.b  #':',d1
        jsr             DisplayChar
        move.l  a0,d1
        jsr             DisplayWord
        moveq   #7,d2
dspmem1:
        move.b  #' ',d1
        jsr             DisplayChar
        move.b  (a0)+,d1
        jsr             DisplayByte
        dbra    d2,dspmem1
        jmp             CRLF

;==============================================================================
; Monitor
;==============================================================================
;
StartMon:
Monitor:
;       lea             STACK,a7                ; reset the stack pointer
        clr.w   KeybdEcho               ; turn off keyboard echo
PromptLn:
        bsr             CRLF
        move.b  #'$',d1
        bsr             DisplayChar

; Get characters until a CR is keyed
;
Prompt3:
        bsr             GetKey
        cmpi.b  #CR,d1
        beq             Prompt1
        bsr             DisplayChar
        bra             Prompt3

; Process the screen line that the CR was keyed on
;
Prompt1:
        clr.w   CursorCol               ; go back to the start of the line
        bsr             CalcScreenLoc   ; a0 = screen memory location
        move.w  (a0)+,d1
        bsr             ScreenToAscii
        cmpi.b  #'$',d1                 ; skip over '$' prompt character
        bne             Prompt2
        move.w  (a0)+,d1
        bsr             ScreenToAscii
        
; Dispatch based on command character
;
Prompt2:
        cmpi.b  #':',d1                 ; $: - edit memory
        beq             EditMem
        cmpi.b  #'D',d1                 ; $D - dump memory
        beq             DumpMem
        cmpi.b  #'B',d1                 ; $B - start tiny basic
        beq             START
        cmpi.b  #'J',d1                 ; $J - execute code
        beq             ExecuteCode
        cmpi.b  #'L',d1                 ; $L - load S19 file
        beq             LoadS19
        cmpi.b  #'?',d1                 ; $? - display help
        beq             DisplayHelp
        cmpi.b  #'C',d1                 ; $C - clear screen
        beq             TestCLS
        bra             Monitor

TestCLS:
        move.w  (a0)+,d1
        bsr             ScreenToAscii
        cmpi.b  #'L',d1
        bne             Monitor
        move.w  (a0)+,d1
        bsr             ScreenToAscii
        cmpi.b  #'S',d1
        bne             Monitor
        bsr             ClearScreen
        bra             Monitor
        
DisplayHelp:
        lea             HelpMsg,a1
        jsr             DisplayString
        bra             Monitor

HelpMsg:
        dc.b    "? = Display help",CR,LF
        dc.b    "CLS = clear screen",CR,LF
        dc.b    ": = Edit memory bytes",CR,LF
        dc.b    "L = Load S19 file",CR,LF
        dc.b    "D = Dump memory",CR,LF
        dc.b    "B = start tiny basic",CR,LF
        dc.b    "J = Jump to code",CR,LF,0
        even

;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
;
ignBlanks:
        move.w  (a0)+,d1
        bsr             ScreenToAscii
        cmpi.b  #' ',d1
        beq             ignBlanks
        subq    #2,a0
        rts

;------------------------------------------------------------------------------
; Edit memory byte.
;------------------------------------------------------------------------------
;
EditMem:
        bsr             ignBlanks
        bsr             GetHexNumber
        move.l  d1,a1
edtmem1:
        bsr             ignBlanks
        bsr             GetHexNumber
        move.b  d1,(a1)+
        bsr             ignBlanks
        bsr             GetHexNumber
        move.b  d1,(a1)+
        bsr             ignBlanks
        bsr             GetHexNumber
        move.b  d1,(a1)+
        bsr             ignBlanks
        bsr             GetHexNumber
        move.b  d1,(a1)+
        bsr             ignBlanks
        bsr             GetHexNumber
        move.b  d1,(a1)+
        bsr             ignBlanks
        bsr             GetHexNumber
        move.b  d1,(a1)+
        bsr             ignBlanks
        bsr             GetHexNumber
        move.b  d1,(a1)+
        bsr             ignBlanks
        bsr             GetHexNumber
        move.b  d1,(a1)+
        bra             Monitor

;------------------------------------------------------------------------------
; Execute code at the specified address.
;------------------------------------------------------------------------------
;
ExecuteCode:
        bsr             ignBlanks
        bsr             GetHexNumber
        move.l  d1,a0
        jsr             (a0)
        bra     Monitor

;------------------------------------------------------------------------------
; Do a memory dump of the requested location.
;------------------------------------------------------------------------------
;
DumpMem:
        bsr             ignBlanks
        bsr             GetHexNumber
        move.l  d1,a0
        jsr             CRLF
        bsr             DisplayMem
        bsr             DisplayMem
        bsr             DisplayMem
        bsr             DisplayMem
        bsr             DisplayMem
        bsr             DisplayMem
        bsr             DisplayMem
        bsr             DisplayMem
        bra             Monitor

;------------------------------------------------------------------------------
; Get a hexidecimal number. Maximum of eight digits.
;------------------------------------------------------------------------------
;
GetHexNumber:
        movem.l d0/d2,-(a7)
        clr.l   d2
        moveq   #7,d0
gthxn2:
        move.w  (a0)+,d1
        bsr             ScreenToAscii
        bsr             AsciiToHexNybble
        cmp.b   #0xff,d1
        beq             gthxn1
        lsl.l   #4,d2
        andi.l  #0x0f,d1
        or.l    d1,d2
        dbra    d0,gthxn2
gthxn1:
        move.l  d2,d1
        movem.l (a7)+,d0/d2
        rts     

;------------------------------------------------------------------------------
; Convert ASCII character in the range '0' to '9', 'a' tr 'f' or 'A' to 'F'
; to a hex nybble.
;------------------------------------------------------------------------------
;
AsciiToHexNybble:
        cmpi.b  #'0',d1
        blo             gthx3
        cmpi.b  #'9',d1
        bhi             gthx5
        subi.b  #'0',d1
        rts
gthx5:
        cmpi.b  #'A',d1
        blo             gthx3
        cmpi.b  #'F',d1
        bhi             gthx6
        subi.b  #'A',d1
        addi.b  #10,d1
        rts
gthx6:
        cmpi.b  #'a',d1
        blo             gthx3
        cmpi.b  #'f',d1
        bhi             gthx3
        subi.b  #'a',d1
        addi.b  #10,d1
        rts
gthx3:
        moveq   #-1,d1          ; not a hex number
        rts

;==============================================================================
; Load an S19 format file
;==============================================================================
;
LoadS19:
        bra             ProcessRec
NextRec:
        bsr             sGetChar
        cmpi.b  #LF,d0
        bne             NextRec
ProcessRec
        bsr             sGetChar
        move.b  d0,d4
        cmpi.b  #26,d4          ; CTRL-Z ?
        beq             Monitor
        cmpi.b  #'S',d4
        bne             NextRec
        bsr             sGetChar
        move.b  d0,d4
        cmpi.b  #'0',d4
        blo             NextRec
        cmpi.b  #'9',d4         ; d4 = record type
        bhi             NextRec
        bsr             sGetChar
        bsr             AsciiToHexNybble
        move.b  d1,d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.b   #4,d2
        or.b    d2,d1           ; d1 = byte count
        move.b  d1,d3           ; d3 = byte count
        cmpi.b  #'0',d4         ; manufacturer ID record, ignore
        beq             NextRec
        cmpi.b  #'1',d4
        beq             ProcessS1
        cmpi.b  #'2',d4
        beq             ProcessS2
        cmpi.b  #'3',d4
        beq             ProcessS3
        cmpi.b  #'5',d4         ; record count record, ignore
        beq             NextRec
        cmpi.b  #'7',d4
        beq             ProcessS7
        cmpi.b  #'8',d4
        beq             ProcessS8
        cmpi.b  #'9',d4
        beq             ProcessS9
        bra             NextRec

pcssxa
        andi.w  #0xff,d3
        subi.w  #1,d3                   ; one less for dbra
pcss1a
        clr.l   d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
        move.b  d2,(a1)+
        dbra    d3,pcss1a
; Get the checksum byte
        clr.l   d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
        bra             NextRec

ProcessS1:
        bsr             S19Get16BitAddress
        bra             pcssxa
ProcessS2:
        bsr             S19Get24BitAddress
        bra             pcssxa
ProcessS3:
        bsr             S19Get32BitAddress
        bra             pcssxa
ProcessS7:
        bsr             S19Get32BitAddress
        move.l  a1,S19StartAddress
        bra             Monitor
ProcessS8:
        bsr             S19Get24BitAddress
        move.l  a1,S19StartAddress
        bra             Monitor
ProcessS9:
        bsr             S19Get16BitAddress
        move.l  a1,S19StartAddress
        bra             Monitor

S19Get16BitAddress:
        clr.l   d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        move.b  d1,d2
        bra             S1932b

S19Get24BitAddress:
        clr.l   d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        move.b  d1,d2
        bra             S1932a

S19Get32BitAddress:
        clr.l   d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        move.b  d1,d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
S1932a:
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
S1932b:
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
        bsr             sGetChar
        bsr             AsciiToHexNybble
        lsl.l   #4,d2
        or.b    d1,d2
        clr.l   d4
        move.l  d2,a1
        rts

;------------------------------------------------------------------------------
; Get a character from auxillary input, checking the keyboard status for a
; CTRL-C
;------------------------------------------------------------------------------
;
sGetChar:
        bsr             CheckForKey
        beq             sgc1
        bsr             GetKey
        cmpi.b  #CTRLC,d1
        beq             Monitor
sgc1:
        bsr             AUXIN
        beq             sGetChar
        move.b  d0,d1
        rts

;==============================================================================
;==============================================================================

;------------------------------------------------------------------------------
; Sound a tone for a second.
;------------------------------------------------------------------------------
;
Beep:
        move.w  #15,PSG_MASTVOL                 ; set master volume
        move.w  #16667,PSG_FREQUENCY0   ; 1000 Hz
        clr.w   PSG_PULSEWIDTH0                 ; not used
        clr.w   PSG_ATTACK0                             ; zero attack time
        clr.w   PSG_DECAY0                              ; zero decay time
        move.w  #255,PSG_SUSTAIN0               ; max sustain level
        clr.w   PSG_RELEASE0                    ; zero release time
        move.w  #0x1104,PSG_CTRL0               ; gate on, output enabled, triangle waveform

        move.l  #8000000,d0                     ; delay a couple of seconds
Beep1:
        sub.l   #1,d0
        bne             Beep1
        clr.w   PSG_CTRL0                               ; shut off the tone
        move.w  #0,PSG_MASTVOL
        rts

;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
;
ReadTemp:
        move.w  #0x5151,d0              ; start conversion command
        move.w  d0,TMPPMOD              ; 
rdtmp1:
        btst    #7,TMPPMOD+3    ; is transfer done ?
        bne             rdtmp1
        
;       clr.l   d0
;       move.l  d0,-(a7)                ; create a space for error code
;       lea             (a7),a1
;       move.l  #1000,d1                ; delay 1000 ms (1 s)
;       move.l  #AOS_DELAY,d0
;       trap    #1
;       move.l  (a7)+,d0                ; pop error code

        ; delay 1 second
        move.l  #8000000,d0
rdtmp2:
        subi.l  #1,d0
        bne             rdtmp2

        move.w  #0xACAC,d0              ; read config reg
        move.w  d0,TMPPMOD
rdtmp4:
        btst    #7,TMPPMOD+3
        bne             rdtmp4
        move.w  TMPPMOD+2,d0
        swap    d0

        move.w  #0xAAAA,d0              ; issue read temp command
        move.w  d0,TMPPMOD
rdtmp3:
        btst    #7,TMPPMOD+3    ; is transfer done ?
        bne             rdtmp3
        move.w  TMPPMOD+2,d0    ; read the temp
        rts


DisplayDecNumber:
        movem.l d0/d1/a1/a5,-(a7)
        move.l  #scratch1,a5
        move.l  d1,d0
        bsr             HEX2DEC
        move.l  #scratch1,a1
        bsr     DisplayString
        movem.l (a7)+,d0/d1/a1/a5
        rts

;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
;
DisplayHexNumber:
        movem.l d0/d2/d3,-(a7)
        move.w  #7,d2           ; number-1 of digits to display
disphnum1:
        move.b  d1,d0           ; get digit into d0.b
        andi.w  #0x0f,d0
        cmpi.w  #0x09,d0
        bls             disphnum2
        addi.w  #0x7,d0
disphnum2:
        addi.w  #0x130,d0       ; convert to display char
        move.w  d2,d3           ; char count into d3
        asl.w   #1,d3           ; scale * 2
        move.w  d0,(a1,d3.w)
        ror.l   #4,d1           ; rot to next digit
        dbeq    d2,disphnum1
        movem.l (a7)+,d0/d2/d3
        rts

;===============================================================================
;    Perform ram test. (Uses checkerboard testing).
; 
;    Return address must be stored in a3 since the stack cannot be used (it
; would get overwritten in test). Note this routine uses no ram at all.
;===============================================================================
ramtest:
        movea.l #8,a0
        move.l #0xaaaa5555,d0
;-----------------------------------------------------------
;   Write checkerboard pattern to ram then read it back to
; find the highest usable ram address (maybe). This address
; must be lower than the start of the rom (0xe00000).
;-----------------------------------------------------------
        lea             TEXTSCR+20,a1
ramtest1:
        move.l  d0,(a0)
        cmp.l   (a0)+,d0
        bne.s   ramtest6
        move.l  a0,d1
        tst.w   d1
        bne             rmtst1
        jsr             DisplayHexNumber
rmtst1:
        cmpa.l  #0xFFFFFC,a0
        blo.s   ramtest1
;------------------------------------------------------
;   Save maximum useable address for later comparison.
;------------------------------------------------------
ramtest6:
        movea.l a0,a2
        movea.l #8,a0
;--------------------------------------------
;   Read back checkerboard pattern from ram.
;--------------------------------------------
ramtest2:
        move.l (a0)+,d0
        move.l  a0,d1
        tst.w   d1
        bne             rmtst2
        jsr             DisplayHexNumber
rmtst2:
        cmpi.l  #0xaaaa5555,d0
        beq.s   ramtest2
;---------------------------------------
;   Check for matching maximum address.
;---------------------------------------
        cmpa.l a0,a2
        bne.s ramtest7
;---------------------------------------------------
;   The following section does the same test except
; with the checkerboard order switched around.
;---------------------------------------------------
ramtest3:                
        movea.l #8,a0
        move.l  #0x5555aaaa,d0
ramtest4:
        move.l  d0,(a0)
        cmp.l   (a0)+,d0
        bne.s   ramtest8
        move.l  a0,d1
        tst.w   d1
        bne     rmtst3
        jsr             DisplayHexNumber
rmtst3:
        cmpa.l  #0xFFFFFC,a0
        blo.s   ramtest4
ramtest8:
        movea.l a0,a2
        movea.l #8,a0
ramtest5:
        move.l  (a0)+,d0
        move.l  a0,d1
        tst.w   d1
        bne             rmtst4
        jsr             DisplayHexNumber
rmtst4:
        cmpi.l  #0x5555aaaa,d0
        beq.s   ramtest5
        cmpa.l  a0,a2
        bne.s   ramtest7
;---------------------------------------------------
;   Save last ram address in end of memory pointer.
;---------------------------------------------------
        move.l a0,memend
;-----------------------------------
;   Create very first memory block.
;-----------------------------------
        suba.l  #12,a0
        move.l  a0,0x0404
        move.l  #0x46524545,0x0400
        move.l  #0x408,0x408                    ; point back-link to self
        jmp     (a3)
;----------------------------------
; Error in ram - go no farther.
;----------------------------------
ramtest7:
                jmp     (a3)
        bra.s   ramtest7

AddressError:
        movem.l a0/a1/d0/d1/d2/d3,-(a7)
        lea             MSG_ADDRESS_ERROR,a1
        jsr             DisplayString
        movem.l (a7)+,a0/a1/d0/d1/d2/d3
        rte
BusError:
        movem.l a0/a1/d0/d1/d2/d3,-(a7)
        lea             MSG_BUS_ERROR,a1
        jsr             DisplayString
        movem.l (a7)+,a0/a1/d0/d1/d2/d3
        rte
IllegalInstruction:
        movem.l a0/a1/d0/d1/d2/d3,-(a7)
        lea             MSG_ILLEGAL_INSN,a1
        jsr             DisplayString
        movem.l (a7)+,a0/a1/d0/d1/d2/d3
        rte

MSG_ADDRESS_ERROR:
        dc.b    "Address error",0
MSG_BUS_ERROR:
        dc.b    "Bus error",0
MSG_ILLEGAL_INSN:
        dc.b    "Illegal instruction",0
MSG_DIVIDE_BY_ZERO:
        dc.b    "Divide by zero",0
;

;*****************************************************************
;                                                                *
;               Tiny BASIC for the Motorola MC68000              *
;                                                                *
; Derived from Palo Alto Tiny BASIC as published in the May 1976 *
; issue of Dr. Dobb's Journal.  Adapted to the 68000 by:         *
;       Gordon Brandly                                           *
;       12147 - 51 Street                                        *
;       Edmonton AB  T5W 3G8                                     *
;       Canada                                                   *
;       (updated mailing address for 1996)                       *
;                                                                *
; This version is for MEX68KECB Educational Computer Board I/O.  *
;                                                                *
;*****************************************************************
;    Copyright (C) 1984 by Gordon Brandly. This program may be   *
;    freely distributed for personal use only. All commercial    *
;                      rights are reserved.                      *
;*****************************************************************

; Vers. 1.0  1984/7/17  - Original version by Gordon Brandly
;       1.1  1984/12/9  - Addition of '$' print term by Marvin Lipford
;       1.2  1985/4/9   - Bug fix in multiply routine by Rick Murray

;       OPT     FRS,BRS         forward ref.'s & branches default to short

BUFLEN  EQU     80      ;       length of keyboard input buffer

;*
;* Internal variables follow:
;*
        BSS
        ORG             0x600
RANPNT:
        DC.L    START   ;       random number pointer
CURRNT:
        DC.L    1               ;Current line pointer
STKGOS:
        DC.L    1               ;Saves stack pointer in 'GOSUB'
STKINP:
        DC.L    1               ;Saves stack pointer during 'INPUT'
LOPVAR:
        DC.L    1               ;'FOR' loop save area
LOPINC:
        DC.L    1               ;increment
LOPLMT:
        DC.L    1               ;limit
LOPLN:
        DC.L    1               ;line number
LOPPT:
        DC.L    1               ;text pointer
TXTUNF:
        DC.L    1               ;points to unfilled text area
VARBGN:
        DC.L    1               ;points to variable area
STKLMT:
        DC.L    1               ;holds lower limit for stack growth
BUFFER:
        FILL.B  BUFLEN,0x00     ;       Keyboard input buffer

TXT     EQU     $               ;Beginning of program area

        CODE
        even
        ORG     0xFFFF2400
;*
;* Standard jump table. You can change these addresses if you are
;* customizing this interpreter for a different environment.
;*
START:
                BRA.L   CSTART          ;Cold Start entry point
GOWARM: BRA.L   WSTART          ;Warm Start entry point
GOOUT:  BRA.L   OUTC            ;Jump to character-out routine
GOIN:   BRA.L   INC             ;Jump to character-in routine
GOAUXO: BRA.L   AUXOUT          ;Jump to auxiliary-out routine
GOAUXI: BRA.L   AUXIN           ;Jump to auxiliary-in routine
GOBYE:  BRA.L   BYEBYE          ;Jump to monitor, DOS, etc.
;*
;* Modifiable system constants:
;*
; Give Tiny Basic 3MB
TXTBGN  DC.L    0xC00000        ;beginning of program memory
ENDMEM  DC.L    0xF00000        ;       end of available memory
;*
;* The main interpreter starts here:
;*
CSTART:
        LEA             START,A0
        MOVE.L  A0,RANPNT
        MOVE.L  ENDMEM,SP       ;initialize stack pointer
        LEA             INITMSG,A6      ;tell who we are
        BSR.L   PRMESG
        MOVE.L  TXTBGN,TXTUNF   ;init. end-of-program pointer
        MOVE.L  ENDMEM,D0       ;get address of end of memory
        SUB.L   #2048,D0        ;reserve 2K for the stack
        MOVE.L  D0,STKLMT
        SUB.L   #4104,D0        ;reserve variable area (27 long words)
        MOVE.L  D0,VARBGN
WSTART:
        CLR.L   D0              ;initialize internal variables
        MOVE.L  D0,LOPVAR
        MOVE.L  D0,STKGOS
        MOVE.L  D0,CURRNT       ;current line number pointer = 0
        MOVE.L  ENDMEM,SP       ;init S.P. again, just in case
        LEA     OKMSG,A6        ;display "OK"
        BSR.L   PRMESG
ST3:
        MOVE.B  #'>',D0  ;       Monitor with a '>' and
        BSR.L   GETLN   ;       read a line.
        BSR.L   TOUPBUF ;       convert to upper case
        MOVE.L  A0,A4   ;       save pointer to end of line
        LEA     BUFFER,A0       ;point to the beginning of line
        BSR.L   TSTNUM  ;       is there a number there?
        BSR.L   IGNBLK  ;       skip trailing blanks
        TST     D1              ;does line no. exist? (or nonzero?)
        BEQ.L   DIRECT  ;       if not, it's a direct statement
        CMP.L   #0xFFFF,D1      ;see if line no. is <= 16 bits
        BCC.L   QHOW            ;if not, we've overflowed
        MOVE.B  D1,-(A0)        ;store the binary line no.
        ROR     #8,D1           ;(Kludge to store a word on a
        MOVE.B  D1,-(A0)        ;possible byte boundary)
        ROL     #8,D1
        BSR.L   FNDLN   ;       find this line in save area
        MOVE.L  A1,A5   ;       save possible line pointer
        BNE     ST4                             ;       if not found, insert
        BSR.L   FNDNXT          ;find the next line (into A1)
        MOVE.L  A5,A2           ;pointer to line to be deleted
        MOVE.L  TXTUNF,A3       ;points to top of save area
        BSR.L   MVUP            ;move up to delete
        MOVE.L  A2,TXTUNF       ;update the end pointer
ST4:
        MOVE.L  A4,D0           ;calculate the length of new line
        SUB.L   A0,D0
        CMP.L   #3,D0           ;is it just a line no. & CR?
        BEQ     ST3                             ;if so, it was just a delete
        MOVE.L  TXTUNF,A3       ;compute new end
        MOVE.L  A3,A6
        ADD.L   D0,A3
        MOVE.L  VARBGN,D0       ;see if there's enough room
        CMP.L   A3,D0
        BLS.L   QSORRY          ;if not, say so
        MOVE.L  A3,TXTUNF       ;if so, store new end position
        MOVE.L  A6,A1           ;points to old unfilled area
        MOVE.L  A5,A2           ;points to beginning of move area
        BSR.L   MVDOWN          ;move things out of the way
        MOVE.L  A0,A1           ;set up to do the insertion
        MOVE.L  A5,A2
        MOVE.L  A4,A3
        BSR.L   MVUP            ;do it
        BRA     ST3             ;go back and get another line

;*
;*******************************************************************
;*
;* *** Tables *** DIRECT *** EXEC ***
;*
;* This section of the code tests a string against a table. When
;* a match is found, control is transferred to the section of
;* code according to the table.
;*
;* At 'EXEC', A0 should point to the string, A1 should point to
;* the character table, and A2 should point to the execution
;* table. At 'DIRECT', A0 should point to the string, A1 and
;* A2 will be set up to point to TAB1 and TAB1.1, which are
;* the tables of all direct and statement commands.
;*
;* A '.' in the string will terminate the test and the partial
;* match will be considered as a match, e.g. 'P.', 'PR.','PRI.',
;* 'PRIN.', or 'PRINT' will all match 'PRINT'.
;*
;* There are two tables: the character table and the execution
;* table. The character table consists of any number of text items.
;* Each item is a string of characters with the last character's
;* high bit set to one. The execution table holds a 16-bit
;* execution addresses that correspond to each entry in the
;* character table.
;*
;* The end of the character table is a 0 byte which corresponds
;* to the default routine in the execution table, which is
;* executed if none of the other table items are matched.
;*
;* Character-matching tables:
TAB1:
        DC.B    'LIS',('T'+0x80) ;        Direct commands
        DC.B    'LOA',('D'+0x80)
        DC.B    'NE',('W'+0x80)
        DC.B    'RU',('N'+0x80)
        DC.B    'SAV',('E'+0x80)
        DC.B    'CL',('S'+0x80)
TAB2:
        DC.B    'NEX',('T'+0x80)  ;       Direct / statement
        DC.B    'LE',('T'+0x80)
        DC.B    'I',('F'+0x80)
        DC.B    'GOT',('O'+0x80)
        DC.B    'GOSU',('B'+0x80)
        DC.B    'RETUR',('N'+0x80)
        DC.B    'RE',('M'+0x80)
        DC.B    'FO',('R'+0x80)
        DC.B    'INPU',('T'+0x80)
        DC.B    'PRIN',('T'+0x80)
        DC.B    'POK',('E'+0x80)
        DC.B    'STO',('P'+0x80)
        DC.B    'BY',('E'+0x80)
        DC.B    'CAL',('L'+0x80)
        DC.B    'LIN',('E'+0x80)
        DC.B    'POIN',('T'+0x80)
        DC.B    'PENCOLO',('R'+0x80)
        DC.B    'FILLCOLO',('R'+0x80)
        DC.B    0
TAB4:
        DC.B    'PEE',('K'+0x80)   ;      Functions
        DC.B    'RN',('D'+0x80)
        DC.B    'AB',('S'+0x80)
        DC.B    'SIZ',('E'+0x80)
        DC.B    'TIC',('K'+0x80)
        DC.B    'TEM',('P'+0x80)
        DC.B    'SG',('N'+0x80)
        DC.B    0
TAB5:
        DC.B    'T',('O'+0x80)      ;     "TO" in "FOR"
        DC.B    0
TAB6:
        DC.B    'STE',('P'+0x80)     ;    "STEP" in "FOR"
        DC.B    0
TAB8:
        DC.B    '>',('='+0x80)        ;   Relational operators
        DC.B    '<',('>'+0x80)
        DC.B    ('>'+0x80)
        DC.B    ('='+0x80)
        DC.B    '<',('='+0x80)
        DC.B    ('<'+0x80)
        DC.B    0
;       DC.B    0        ;<- for aligning on a word boundary
        
        even
        
;* Execution address tables:
TAB1_1:
        DC.W    LIST_                   ;Direct commands
        DC.W    LOAD
        DC.W    NEW
        DC.W    RUN
        DC.W    SAVE
        DC.W    CLS
TAB2_1:
        DC.W    NEXT                    ;Direct / statement
        DC.W    LET
        DC.W    IF
        DC.W    GOTO
        DC.W    GOSUB
        DC.W    RETURN
        DC.W    REM
        DC.W    FOR
        DC.W    INPUT
        DC.W    PRINT
        DC.W    POKE
        DC.W    STOP_
        DC.W    GOBYE
        DC.W    CALL
        DC.W    LINE
        DC.W    POINT
        DC.W    PENCOLOR
        DC.W    FILLCOLOR
        DC.W    DEFLT
TAB4_1:
        DC.W    PEEK                    ;Functions
        DC.W    RND
        DC.W    ABS
        DC.W    SIZE_
        DC.W    TICK
        DC.W    TEMP
        DC.W    SGN
        DC.W    XP40
TAB5_1:
        DC.W    FR1             ;       "TO" in "FOR"
        DC.W    QWHAT
TAB6_1:
        DC.W    FR2             ;       "STEP" in "FOR"
        DC.W    FR3
TAB8_1:
        DC.W    XP11;   >=              Relational operators
        DC.W    XP12    ;<>
        DC.W    XP13    ;>
        DC.W    XP15    ;=
        DC.W    XP14    ;<=
        DC.W    XP16    ;<
        DC.W    XP17
;*
DIRECT:
        LEA     TAB1,A1
        LEA     TAB1_1,A2
EXEC:
        BSR.L   IGNBLK;         ignore leading blanks
        MOVE.L  A0,A3           ;save the pointer
        CLR.B   D2              ;clear match flag
EXLP:
        MOVE.B  (A0)+,D0;       get the program character
        MOVE.B  (A1),D1         ;get the table character
        BNE     EXNGO           ;If end of table,
        MOVE.L  A3,A0   ;;      restore the text pointer and...
        BRA     EXGO            ;execute the default.
EXNGO:
        MOVE.B  D0,D3   ;       Else check for period...
        AND.B   D2,D3           ;and a match.
        CMP.B   #'.',D3
        BEQ     EXGO            ;if so, execute
        AND.B   #0x7F,D1 ;      ignore the table's high bit
        CMP.B   D0,D1   ;       is there a match?
        BEQ     EXMAT
        ADDQ.L  #2,A2   ;       if not, try the next entry
        MOVE.L  A3,A0   ;       reset the program pointer
        CLR.B   D2              ;sorry, no match
EX1:
        TST.B   (A1)+   ;       get to the end of the entry
        BPL     EX1
        BRA     EXLP            ;back for more matching
EXMAT:
        MOVEQ   #-1,D2;         we've got a match so far
        TST.B   (A1)+   ;       end of table entry?
        BPL     EXLP            ;if not, go back for more
EXGO:
        LEA             0xFFFF0000,A3   ;       execute the appropriate routine
        move.w  (a2),a2
        JMP     (A3,A2.W)

CLS:
        jsr             ClearScreen
        clr.w   CursorRow
        clr.w   CursorCol
        bra             WSTART
;*
;*******************************************************************
;*
;* What follows is the code to execute direct and statement
;* commands. Control is transferred to these points via the command
;* table lookup code of 'DIRECT' and 'EXEC' in the last section.
;* After the command is executed, control is transferred to other
;* sections as follows:
;*
;* For 'LIST', 'NEW', and 'STOP': go back to the warm start point.
;* For 'RUN': go execute the first stored line if any; else go
;* back to the warm start point.
;* For 'GOTO' and 'GOSUB': go execute the target line.
;* For 'RETURN' and 'NEXT'; go back to saved return line.
;* For all others: if 'CURRNT' is 0, go to warm start; else go;
;* execute next command. (This is done in 'FINISH'.)
;*
;*******************************************************************
;*
;* *** NEW *** STOP *** RUN (& friends) *** GOTO ***
;*
;* 'NEW<CR>' sets TXTUNF to point to TXTBGN
;*
;* 'STOP<CR>' goes back to WSTART
;*
;* 'RUN<CR>' finds the first stored line, stores its address
;* in CURRNT, and starts executing it. Note that only those
;* commands in TAB2 are legal for a stored program.
;*
;* There are 3 more entries in 'RUN':
;* 'RUNNXL' finds next line, stores it's address and executes it.
;* 'RUNTSL' stores the address of this line and executes it.
;* 'RUNSML' continues the execution on same line.
;*
;* 'GOTO expr<CR>' evaluates the expression, finds the target
;* line, and jumps to 'RUNTSL' to do it.
;*
NEW:
        BSR.L   ENDCHK
        MOVE.L  TXTBGN,TXTUNF   ;set the end pointer

STOP_:
        BSR.L   ENDCHK
        BRA     WSTART

RUN:
        BSR.L   ENDCHK
        MOVE.L  TXTBGN,A0       ;set pointer to beginning
        MOVE.L  A0,CURRNT

RUNNXL:
        TST.L   CURRNT  ;       executing a program?
        BEQ.L   WSTART          ;if not, we've finished a direct stat.
        CLR.L   D1              ;else find the next line number
        MOVE.L  A0,A1
        BSR.L   FNDLNP
        BCS     WSTART          ;if we've fallen off the end, stop

RUNTSL:
        MOVE.L  A1,CURRNT       ;set CURRNT to point to the line no.
        MOVE.L  A1,A0           ;set the text pointer to
        ADDQ.L  #2,A0           ;the start of the line text

RUNSML:
        BSR.L   CHKIO   ;       see if a control-C was pressed
        LEA     TAB2,A1         ;find command in TAB2
        LEA     TAB2_1,A2
        BRA     EXEC            ;and execute it

GOTO:
        BSR.L   EXPR    ;       evaluate the following expression
        BSR.L   ENDCHK          ;must find end of line
        MOVE.L  D0,D1
        BSR.L   FNDLN           ;find the target line
        BNE.L   QHOW            ;no such line no.
        BRA     RUNTSL          ;go do it

;*
;*******************************************************************
;*
;* *** LIST *** PRINT ***
;*
;* LIST has two forms:
;* 'LIST<CR>' lists all saved lines
;* 'LIST #<CR>' starts listing at the line #
;* Control-S pauses the listing, control-C stops it.
;*
;* PRINT command is 'PRINT ....:' or 'PRINT ....<CR>'
;* where '....' is a list of expressions, formats, back-arrows,
;* and strings. These items a separated by commas.
;*
;* A format is a pound sign followed by a number.  It controls
;* the number of spaces the value of an expression is going to
;* be printed in.  It stays effective for the rest of the print
;* command unless changed by another format.  If no format is
;* specified, 11 positions will be used.
;*
;* A string is quoted in a pair of single- or double-quotes.
;*
;* An underline (back-arrow) means generate a <CR> without a <LF>
;*
;* A <CR LF> is generated after the entire list has been printed
;* or if the list is empty.  If the list ends with a semicolon,
;* however, no <CR LF> is generated.
;*

LIST_:
        BSR.L   TSTNUM  ;       see if there's a line no.
        BSR.L   ENDCHK          ;if not, we get a zero
        BSR.L   FNDLN           ;find this or next line
LS1:
        BCS     WSTART          ;warm start if we passed the end
        BSR.L   PRTLN   ;       print the line
        BSR.L   CHKIO   ;       check for listing halt request
        BEQ     LS3
        CMP.B   #CTRLS,D0       ;pause the listing?
        BNE     LS3
LS2:
        BSR.L   CHKIO           ;if so, wait for another keypress
        BEQ     LS2
LS3:
        BSR.L   FNDLNP          ;find the next line
        BRA     LS1

PRINT:
        MOVE    #11,D4  ;       D4 = number of print spaces
        BSR.L   TSTC            ;if null list and ":"
        DC.B    ':',PR2-$
        BSR.L   CRLF1           ;give CR-LF and continue
        BRA     RUNSML          ;execution on the same line
PR2:
        BSR.L   TSTC            ;if null list and <CR>
        DC.B    CR,PR0-$
        BSR.L   CRLF1           ;also give CR-LF and
        BRA     RUNNXL          ;execute the next line
PR0:
        BSR.L   TSTC            ;else is it a format?
        DC.B    '#',PR1-$
        BSR.L   EXPR            ;yes, evaluate expression
        MOVE    D0,D4           ;and save it as print width
        BRA     PR3             ;look for more to print
PR1:
        BSR.L   TSTC            ;is character expression? (MRL)
        DC.B    '$',PR4-$
        BSR.L   EXPR            ;yep. Evaluate expression (MRL)
        BSR     GOOUT           ;print low byte (MRL)
        BRA     PR3             ;look for more. (MRL)
PR4:
        BSR.L   QTSTG   ;       is it a string?
        BRA.S   PR8             ;if not, must be an expression
PR3:
        BSR.L   TSTC    ;       if ",", go find next
        DC.B    ',',PR6-$
        BSR.L   FIN             ;in the list.
        BRA     PR0
PR6:
        BSR.L   CRLF1   ;       list ends here
        BRA     FINISH
PR8:
        MOVE    D4,-(SP)        ;save the width value
        BSR.L   EXPR            ;evaluate the expression
        MOVE    (SP)+,D4        ;restore the width
        MOVE.L  D0,D1
        BSR.L   PRTNUM          ;print its value
        BRA     PR3             ;more to print?

FINISH:
        BSR.L   FIN     ;       Check end of command
        BRA.L   QWHAT   ;       print "What?" if wrong

;*
;*******************************************************************
;*
;* *** GOSUB *** & RETURN ***
;*
;* 'GOSUB expr:' or 'GOSUB expr<CR>' is like the 'GOTO' command,
;* except that the current text pointer, stack pointer, etc. are
;* saved so that execution can be continued after the subroutine
;* 'RETURN's.  In order that 'GOSUB' can be nested (and even
;* recursive), the save area must be stacked.  The stack pointer
;* is saved in 'STKGOS'.  The old 'STKGOS' is saved on the stack.
;* If we are in the main routine, 'STKGOS' is zero (this was done
;* in the initialization section of the interpreter), but we still
;* save it as a flag for no further 'RETURN's.
;*
;* 'RETURN<CR>' undoes everything that 'GOSUB' did, and thus
;* returns the execution to the command after the most recent
;* 'GOSUB'.  If 'STKGOS' is zero, it indicates that we never had
;* a 'GOSUB' and is thus an error.
;*
GOSUB:
        BSR.L   PUSHA   ;       save the current 'FOR' parameters
        BSR.L   EXPR            ;get line number
        MOVE.L  A0,-(SP)        ;save text pointer
        MOVE.L  D0,D1
        BSR.L   FNDLN           ;find the target line
        BNE.L   AHOW            ;if not there, say "How?"
        MOVE.L  CURRNT,-(SP)    ;found it, save old 'CURRNT'...
        MOVE.L  STKGOS,-(SP)    ;and 'STKGOS'
        CLR.L   LOPVAR          ;load new values
        MOVE.L  SP,STKGOS
        BRA     RUNTSL

RETURN:
        BSR.L   ENDCHK  ;       there should be just a <CR>
        MOVE.L  STKGOS,D1       ;get old stack pointer
        BEQ.L   QWHAT           ;if zero, it doesn't exist
        MOVE.L  D1,SP           ;else restore it
        MOVE.L  (SP)+,STKGOS    ;and the old 'STKGOS'
        MOVE.L  (SP)+,CURRNT    ;and the old 'CURRNT'
        MOVE.L  (SP)+,A0        ;and the old text pointer
        BSR.L   POPA            ;and the old 'FOR' parameters
        BRA     FINISH          ;and we are back home

;*
;*******************************************************************
;*
;* *** FOR *** & NEXT ***
;*
;* 'FOR' has two forms:
;* 'FOR var=exp1 TO exp2 STEP exp1' and 'FOR var=exp1 TO exp2'
;* The second form means the same thing as the first form with a
;* STEP of positive 1.  The interpreter will find the variable 'var'
;* and set its value to the current value of 'exp1'.  It also
;* evaluates 'exp2' and 'exp1' and saves all these together with
;* the text pointer, etc. in the 'FOR' save area, which consisits of
;* 'LOPVAR', 'LOPINC', 'LOPLMT', 'LOPLN', and 'LOPPT'.  If there is
;* already something in the save area (indicated by a non-zero
;* 'LOPVAR'), then the old save area is saved on the stack before
;* the new values are stored.  The interpreter will then dig in the
;* stack and find out if this same variable was used in another
;* currently active 'FOR' loop.  If that is the case, then the old
;* 'FOR' loop is deactivated. (i.e. purged from the stack)
;*
;* 'NEXT var' serves as the logical (not necessarily physical) end
;* of the 'FOR' loop.  The control variable 'var' is checked with
;* the 'LOPVAR'.  If they are not the same, the interpreter digs in
;* the stack to find the right one and purges all those that didn't
;* match.  Either way, it then adds the 'STEP' to that variable and
;* checks the result with against the limit value.  If it is within
;* the limit, control loops back to the command following the
;* 'FOR'.  If it's outside the limit, the save area is purged and
;* execution continues.
;*
FOR:
        BSR.L   PUSHA           ;save the old 'FOR' save area
        BSR.L   SETVAL          ;set the control variable
        MOVE.L  A6,LOPVAR       ;save its address
        LEA     TAB5,A1         ;use 'EXEC' to test for 'TO'
        LEA     TAB5_1,A2
        BRA     EXEC
FR1:
        BSR.L   EXPR            ;evaluate the limit
        MOVE.L  D0,LOPLMT       ;save that
        LEA     TAB6,A1         ;use 'EXEC' to look for the
        LEA     TAB6_1,A2       ;word 'STEP'
        BRA     EXEC
FR2:
        BSR.L   EXPR    ;       found it, get the step value
        BRA     FR4
FR3:
        MOVEQ   #1,D0   ;       not found, step defaults to 1
FR4:
        MOVE.L  D0,LOPINC       ;save that too
FR5:
        MOVE.L  CURRNT,LOPLN    ;save address of current line number
        MOVE.L  A0,LOPPT        ;and text pointer
        MOVE.L  SP,A6           ;dig into the stack to find 'LOPVAR'
        BRA     FR7
FR6:
        ADD.L   #20,A6          ;look at next stack frame
FR7:
        MOVE.L  (A6),D0         ;is it zero?
        BEQ     FR8             ;if so, we're done
        CMP.L   LOPVAR,D0       ;same as current LOPVAR?
        BNE     FR6             ;nope, look some more
        MOVE.L  SP,A2   ;       Else remove 5 long words from...
        MOVE.L  A6,A1   ;       inside the stack.
        LEA     20,A3
        ADD.L   A1,A3
        BSR.L   MVDOWN
        MOVE.L  A3,SP   ;       set the SP 5 long words up
FR8:
        BRA     FINISH          ;and continue execution

NEXT:
        BSR.L   TSTV;           get address of variable
        BCS.L   QWHAT   ;       if no variable, say "What?"
        MOVE.L  D0,A1   ;       save variable's address
NX0:
        MOVE.L  LOPVAR,D0;      If 'LOPVAR' is zero, we never...
        BEQ.L   QWHAT   ;       had a FOR loop, so say "What?"
        CMP.L   D0,A1   ;;      else we check them
        BEQ     NX3     ;       OK, they agree
        BSR.L   POPA    ;       nope, let's see the next frame
        BRA     NX0
NX3:
        MOVE.L  (A1),D0 ;       get control variable's value
        ADD.L   LOPINC,D0;      add in loop increment
        BVS.L   QHOW    ;       say "How?" for 32-bit overflow
        MOVE.L  D0,(A1) ;       save control variable's new value
        MOVE.L  LOPLMT,D1;      get loop's limit value
        TST.L   LOPINC
        BPL     NX1     ;       branch if loop increment is positive
        EXG     D0,D1
NX1:
        CMP.L   D0,D1;          test against limit
        BLT     NX2;            branch if outside limit
        MOVE.L  LOPLN,CURRNT    ;Within limit, go back to the...
        MOVE.L  LOPPT,A0        ;saved 'CURRNT' and text pointer.
        BRA     FINISH
NX2:
        BSR.L   POPA            ;purge this loop
        BRA     FINISH

;*
;*******************************************************************
;*
;* *** REM *** IF *** INPUT *** LET (& DEFLT) ***
;*
;* 'REM' can be followed by anything and is ignored by the
;* interpreter.
;*
;* 'IF' is followed by an expression, as a condition and one or
;* more commands (including other 'IF's) separated by colons.
;* Note that the word 'THEN' is not used.  The interpreter evaluates
;* the expression.  If it is non-zero, execution continues.  If it
;* is zero, the commands that follow are ignored and execution
;* continues on the next line.
;*
;* 'INPUT' is like the 'PRINT' command, and is followed by a list
;* of items.  If the item is a string in single or double quotes,
;* or is an underline (back arrow), it has the same effect as in
;* 'PRINT'.  If an item is a variable, this variable name is
;* printed out followed by a colon, then the interpreter waits for
;* an expression to be typed in.  The variable is then set to the
;* value of this expression.  If the variable is preceeded by a
;* string (again in single or double quotes), the string will be
;* displayed followed by a colon.  The interpreter the waits for an
;* expression to be entered and sets the variable equal to the
;* expression's value.  If the input expression is invalid, the
;* interpreter will print "What?", "How?", or "Sorry" and reprint
;* the prompt and redo the input.  The execution will not terminate
;* unless you press control-C.  This is handled in 'INPERR'.
;*
;* 'LET' is followed by a list of items separated by commas.
;* Each item consists of a variable, an equals sign, and an
;* expression.  The interpreter evaluates the expression and sets
;* the variable to that value.  The interpreter will also handle
;* 'LET' commands without the word 'LET'.  This is done by 'DEFLT'.
;*
REM:
        BRA     IF2             ;skip the rest of the line

IF:     
        BSR.L   EXPR    ;       evaluate the expression
IF1:
        TST.L   D0              ;is it zero?
        BNE     RUNSML          ;if not, continue
IF2:
        MOVE.L  A0,A1
        CLR.L   D1
        BSR.L   FNDSKP  ;       if so, skip the rest of the line
        BCC     RUNTSL          ;and run the next line
        BRA.L   WSTART  ;       if no next line, do a warm start

INPERR:
        MOVE.L  STKINP,SP;      restore the old stack pointer
        MOVE.L  (SP)+,CURRNT;   and old 'CURRNT'
        ADDQ.L  #4,SP
        MOVE.L  (SP)+,A0        ;and old text pointer

INPUT:
        MOVE.L  A0,-(SP);       save in case of error
        BSR.L   QTSTG           ;is next item a string?
        BRA.S   IP2             ;nope
        BSR.L   TSTV    ;       yes, but is it followed by a variable?
        BCS     IP4             ;if not, branch
        MOVE.L  D0,A2   ;       put away the variable's address
        BRA     IP3             ;if so, input to variable
IP2:
        MOVE.L  A0,-(SP);       save for 'PRTSTG'
        BSR.L   TSTV    ;       must be a variable now
        BCS.L   QWHAT   ;       "What?" it isn't?
        MOVE.L  D0,A2   ;       put away the variable's address
        MOVE.B  (A0),D2 ;       get ready for 'PRTSTG'
        CLR.B   D0
        MOVE.B  D0,(A0)
        MOVE.L  (SP)+,A1
        BSR.L   PRTSTG  ;       print string as prompt
        MOVE.B  D2,(A0) ;       restore text
IP3:
        MOVE.L  A0,-(SP);       save in case of error
        MOVE.L  CURRNT,-(SP)    ;also save 'CURRNT'
        MOVE.L  #-1,CURRNT      ;flag that we are in INPUT
        MOVE.L  SP,STKINP       ;save the stack pointer too
        MOVE.L  A2,-(SP)        ;save the variable address
        MOVE.B  #':',D0     ;    print a colon first
        BSR.L   GETLN           ;then get an input line
        LEA     BUFFER,A0       ;point to the buffer
        BSR.L   EXPR    ;       evaluate the input
        MOVE.L  (SP)+,A2        ;restore the variable address
        MOVE.L  D0,(A2)         ;save value in variable
        MOVE.L  (SP)+,CURRNT    ;restore old 'CURRNT'
        MOVE.L  (SP)+,A0;       and the old text pointer
IP4:
        ADDQ.L  #4,SP   ;       clean up the stack
        BSR.L   TSTC    ;       is the next thing a comma?
        DC.B    ',',IP5-$
        BRA     INPUT   ;       yes, more items
IP5:
        BRA     FINISH

DEFLT:
        CMP.B   #CR,(A0);       empty line is OK
        BEQ     LT1             ;else it is 'LET'

LET:
        BSR.L   SETVAL          ;do the assignment
        BSR.L   TSTC            ;check for more 'LET' items
        DC.B    ',',LT1-$
        BRA     LET
LT1:
        BRA     FINISH          ;until we are finished.

;*
;*******************************************************************
;*
;* *** LOAD *** & SAVE ***
;*
;* These two commands transfer a program to/from an auxiliary
;* device such as a cassette, another computer, etc.  The program
;* is converted to an easily-stored format: each line starts with
;* a colon, the line no. as 4 hex digits, and the rest of the line.
;* At the end, a line starting with an '@' sign is sent.  This
;* format can be read back with a minimum of processing time by
;* the 68000.
;*
LOAD:
        MOVE.L  TXTBGN,A0       ;set pointer to start of prog. area
        MOVE.B  #CR,D0          ;For a CP/M host, tell it we're ready...
        BSR     GOAUXO          ;by sending a CR to finish PIP command.
LOD1:
        BSR     GOAUXI  ;       look for start of line
        BEQ     LOD1
        CMP.B   #'@',D0  ;       end of program?
        BEQ     LODEND
        CMP.B   #':',D0   ;      if not, is it start of line?
        BNE     LOD1                    ;if not, wait for it
        BSR     GBYTE                   ;get first byte of line no.
        MOVE.B  D1,(A0)+        ;store it
        BSR     GBYTE                   ;get 2nd bye of line no.
        MOVE.B  D1,(A0)+        ;       store that, too
LOD2:
        BSR     GOAUXI  ;       get another text char.
        BEQ     LOD2
        MOVE.B  D0,(A0)+        ;store it
        CMP.B   #CR,D0          ;is it the end of the line?
        BNE     LOD2            ;if not, go back for more
        BRA     LOD1            ;if so, start a new line
LODEND:
        MOVE.L  A0,TXTUNF       ;set end-of program pointer
        BRA     WSTART          ;back to direct mode

GBYTE:
        MOVEQ   #1,D2   ;               get two hex characters from auxiliary
        CLR     D1                      ;and store them as a byte in D1
GBYTE1:
        BSR     GOAUXI          ;       get a char.
        BEQ     GBYTE1
        CMP.B   #'A',D0
        BCS     GBYTE2
        SUBQ.B  #7,D0   ;       if greater than 9, adjust
GBYTE2:
        AND.B   #0xF,D0         ;strip ASCII
        LSL.B   #4,D1           ;put nybble into the result
        OR.B    D0,D1
        DBRA    D2,GBYTE1       ;get another char.
        RTS

SAVE:
        MOVE.L  TXTBGN,A0;      set pointer to start of prog. area
        MOVE.L  TXTUNF,A1       ;set pointer to end of prog. area
SAVE1:
        MOVE.B  #CR,D0  ;       send out a CR & LF (CP/M likes this)
        BSR     GOAUXO
        MOVE.B  #LF,D0
        BSR     GOAUXO
        CMP.L   A0,A1           ;are we finished?
        BLS     SAVEND
        MOVE.B  #':',D0      ;   if not, start a line
        BSR     GOAUXO
        MOVE.B  (A0)+,D1        ;send first half of line no.
        BSR     PBYTE
        MOVE.B  (A0)+,D1        ;and send 2nd half
        BSR     PBYTE
SAVE2:
        MOVE.B  (A0)+,D0;       get a text char.
        CMP.B   #CR,D0          ;is it the end of the line?
        BEQ     SAVE1           ;if so, send CR & LF and start new line
        BSR     GOAUXO          ;send it out
        BRA     SAVE2           ;go back for more text
SAVEND:
        MOVE.B  #'@',D0 ;        send end-of-program indicator
        BSR     GOAUXO
        MOVE.B  #CR,D0  ;       followed by a CR & LF
        BSR     GOAUXO
        MOVE.B  #LF,D0
        BSR     GOAUXO
        MOVE.B  #0x1A,D0        ;and a control-Z to end the CP/M file
        BSR     GOAUXO
        BRA     WSTART          ;then go do a warm start

PBYTE:
        MOVEQ   #1,D2   ;       send two hex characters from D1's low byte
PBYTE1:
        ROL.B   #4,D1   ;       get the next nybble
        MOVE.B  D1,D0
        AND.B   #0xF,D0 ;       strip off garbage
        ADD.B   #'0',D0   ;      make it into ASCII
        CMP.B   #'9',D0
        BLS     PBYTE2
        ADDQ.B  #7,D0           ;adjust if greater than 9
PBYTE2:
        BSR     GOAUXO          ;send it out
        DBRA    D2,PBYTE1       ;then send the next nybble
        RTS

;*
;*******************************************************************
;*
;* *** POKE *** & CALL ***
;*
;* 'POKE expr1,expr2' stores the byte from 'expr2' into the memory
;* address specified by 'expr1'.
;*
;* 'CALL expr' jumps to the machine language subroutine whose
;* starting address is specified by 'expr'.  The subroutine can use
;* all registers but must leave the stack the way it found it.
;* The subroutine returns to the interpreter by executing an RTS.
;*
POKE:
        BSR     EXPR            ;get the memory address
        BSR.L   TSTC            ;it must be followed by a comma
        DC.B    ',',PKER-$
        MOVE.L  D0,-(SP)        ;save the address
        BSR     EXPR            ;get the byte to be POKE'd
        MOVE.L  (SP)+,A1        ;get the address back
        MOVE.B  D0,(A1)         ;store the byte in memory
        BRA     FINISH
PKER:
        BRA.L   QWHAT   ;       if no comma, say "What?"

POINT:
        BSR EXPR
        BSR     TSTC
        DC.B    ',',PKER-$
        MOVE.L  D0,-(SP)
        BSR     EXPR
        MOVE.L  (SP)+,D1
        MOVE.L  D0,D2
        BSR DrawPixel
        BRA FINISH

PENCOLOR:
        BSR     EXPR
        MOVE.L  d0,GRAPHICS
        BRA FINISH
FILLCOLOR:
        BSR     EXPR
        MOVE.L  d0,GRAPHICS+4
        BRA FINISH

LINE:
        BSR     EXPR
        BSR     TSTC
        DC.B    ',',LINEERR1-$
        MOVE.L  D0,-(SP)
        BSR     EXPR
        BSR     TSTC
        DC.B    ',',LINEERR2-$
        MOVE.L  D0,-(SP)
        BSR     EXPR
        BSR     TSTC
        DC.B    ',',LINEERR3-$
        MOVE.L  D0,-(SP)
        BSR     EXPR
;       MOVE.L  D0,D4
;       MOVE.L  (SP)+,D3
;       MOVE.L  (SP)+,D2
;       MOVE.L  (SP)+,D1
;       BSR             DrawLine
        MOVE.W  d0,GRAPHICS+14
        MOVE.L  (SP)+,d0
        MOVE.W  d0,GRAPHICS+12
        MOVE.L  (SP)+,d0
        MOVE.W  d0,GRAPHICS+10
        MOVE.L  (SP)+,d0
        MOVE.W  d0,GRAPHICS+8
        MOVE.W  #G_DRAWLINE,GRAPHICS+30
        BRA             FINISH
        
LINEERR1:
        BRA.L   QWHAT
LINEERR2:
        ADDQ    #4,SP
        BRA.L   QWHAT
LINEERR3:
        ADD.L   #8,SP
        BRA.L   QWHAT

CALL:
        BSR     EXPR            ;get the subroutine's address
        TST.L   D0              ;make sure we got a valid address
        BEQ.L   QHOW    ;       if not, say "How?"
        MOVE.L  A0,-(SP);       save the text pointer
        MOVE.L  D0,A1
        JSR     (A1)            ;jump to the subroutine
        MOVE.L  (SP)+,A0        ;restore the text pointer
        BRA     FINISH
;*
;*******************************************************************
;*
;* *** EXPR ***
;*
;* 'EXPR' evaluates arithmetical or logical expressions.
;* <EXPR>::=<EXPR2>
;*         <EXPR2><rel.op.><EXPR2>
;* where <rel.op.> is one of the operators in TAB8 and the result
;* of these operations is 1 if true and 0 if false.
;* <EXPR2>::=(+,-,&,|)<EXPR3>(+,-,&,|)<EXPR3>(...
;* where () are optional and (... are optional repeats.
;* <EXPR3>::=<EXPR4>( <* or /><EXPR4> )(...
;* <EXPR4>::=<variable>
;*          <function>
;*          (<EXPR>)
;* <EXPR> is recursive so that the variable '@' can have an <EXPR>
;* as an index, functions can have an <EXPR> as arguments, and
;* <EXPR4> can be an <EXPR> in parenthesis.
;*
EXPR:
        BSR     EXPR2
        MOVE.L  D0,-(SP);       save <EXPR2> value
        LEA     TAB8,A1         ;look up a relational operator
        LEA     TAB8_1,A2
        BRA     EXEC            ;go do it

XP11:
        BSR     XP18    ;       is it ">="?
        BLT     XPRT0           ;no, return D0=0
        BRA     XPRT1           ;else return D0=1

XP12:
        BSR     XP18    ;       is it "<>"?
        BEQ     XPRT0           ;no, return D0=0
        BRA     XPRT1           ;else return D0=1

XP13:
        BSR     XP18    ;       is it ">"?
        BLE     XPRT0           ;no, return D0=0
        BRA     XPRT1           ;else return D0=1

XP14:
        BSR     XP18    ;       is it "<="?
        BGT     XPRT0           ;no, return D0=0
        BRA     XPRT1           ;else return D0=1

XP15:
        BSR     XP18    ;       is it "="?
        BNE     XPRT0           ;if not, return D0=0
        BRA     XPRT1           ;else return D0=1
XP15RT:
        RTS

XP16:
        BSR     XP18    ;       is it "<"?
        BGE     XPRT0           ;if not, return D0=0
        BRA     XPRT1           ;else return D0=1
XP16RT:
        RTS

XPRT0:
        CLR.L   D0      ;       return D0=0 (false)
        RTS

XPRT1:
        MOVEQ   #1,D0;          return D0=1 (true)
        RTS

XP17:
        MOVE.L  (SP)+,D0        ;it's not a rel. operator
        RTS                     ;return D0=<EXPR2>

XP18:
        MOVE.L  (SP)+,D0        ;reverse the top two stack items
        MOVE.L  (SP)+,D1
        MOVE.L  D0,-(SP)
        MOVE.L  D1,-(SP)
        BSR     EXPR2           ;do second <EXPR2>
        MOVE.L  (SP)+,D1
        CMP.L   D0,D1   ;       compare with the first result
        RTS                     ;return the result

EXPR2:
        BSR.L   TSTC            ;negative sign?
        DC.B    '-',XP20-$
        CLR.L   D0      ;       yes, fake '0-'
        BRA     XP26
XP20:
        BSR.L   TSTC
        DC.B    '!',XP21-$
        CLR.L   D0
        MOVE.L  D0,-(SP)
        BSR             EXPR3
        NOT.L   D0
        JMP             XP24
XP21:
        BSR.L   TSTC    ;       positive sign? ignore it
        DC.B    '+',XP22-$
XP22:
        BSR     EXPR3           ;first <EXPR3>
XP23:
        BSR.L   TSTC    ;       add?
        DC.B    '+',XP25-$
        MOVE.L  D0,-(SP)        ;yes, save the value
        BSR     EXPR3           ;get the second <EXPR3>
XP24:
        MOVE.L  (SP)+,D1
        ADD.L   D1,D0   ;       add it to the first <EXPR3>
        BVS.L   QHOW    ;       branch if there's an overflow
        BRA     XP23    ;       else go back for more operations
XP25:
        BSR.L   TSTC            ;subtract?
        DC.B    '-',XP27-$      ; was XP42-$
XP26:
        MOVE.L  D0,-(SP)        ;yes, save the result of 1st <EXPR3>
        BSR     EXPR3           ;get second <EXPR3>
        NEG.L   D0              ;change its sign
        JMP     XP24            ;and do an addition
XP27:
        BSR.L   TSTC
        DC.B    '&',XP28-$
        MOVE.L  D0,-(SP)
        BSR     EXPR3
        MOVE.L  (SP)+,D1
        AND.L   D1,D0
        BRA             XP23
XP28:
        BSR.L   TSTC
        DC.B    '|',XP42-$
        MOVE.L  D0,-(SP)
        BSR     EXPR3
        MOVE.L  (SP)+,D1
        OR.L    D1,D0
        BRA             XP23

EXPR3:
        BSR     EXPR4           ;get first <EXPR4>
XP31:
        BSR.L   TSTC    ;       multiply?
        DC.B    '*',XP34-$
        MOVE.L  D0,-(SP);       yes, save that first result
        BSR     EXPR4           ;get second <EXPR4>
        MOVE.L  (SP)+,D1
        BSR.L   MULT32  ;       multiply the two
        BRA     XP31            ;then look for more terms
XP34:
        BSR.L   TSTC;           divide?
        DC.B    '/',XP42-$
        MOVE.L  D0,-(SP);       save result of 1st <EXPR4>
        BSR     EXPR4           ;get second <EXPR4>
        MOVE.L  (SP)+,D1
        EXG     D0,D1
        BSR.L   DIV32   ;       do the division
        BRA     XP31            ;go back for any more terms

EXPR4:
        LEA     TAB4,A1 ;       find possible function
        LEA     TAB4_1,A2
        BRA     EXEC
XP40:
        BSR     TSTV    ;       nope, not a function
        BCS     XP41            ;nor a variable
        MOVE.L  D0,A1
        CLR.L   D0
        MOVE.L  (A1),D0 ;       if a variable, return its value in D0
EXP4RT:
        RTS
XP41:
        BSR.L   TSTNUM  ;       or is it a number?
        MOVE.L  D1,D0
        TST     D2              ;(if not, # of digits will be zero)
        BNE     EXP4RT  ;       if so, return it in D0
PARN:
        BSR.L   TSTC    ;       else look for ( EXPR )
        DC.B    '(',XP43-$
        BSR     EXPR
        BSR.L   TSTC
        DC.B    ')',XP43-$
XP42:
        RTS
XP43:
        BRA.L   QWHAT   ;       else say "What?"

;*
;* ===== Test for a valid variable name.  Returns Carry=1 if not
;*      found, else returns Carry=0 and the address of the
;*      variable in D0.

TSTV:
        BSR.L   IGNBLK
        CLR.L   D0
        MOVE.B  (A0),D0 ;       look at the program text
        SUB.B   #'@',D0
        BCS     TSTVRT  ;       C=1: not a variable
        BNE     TV1             ;branch if not "@" array
        ADDQ    #1,A0   ;       If it is, it should be
        BSR     PARN            ;followed by (EXPR) as its index.
        ADD.L   D0,D0
        BCS.L   QHOW    ;       say "How?" if index is too big
        ADD.L   D0,D0
        BCS.L   QHOW
        MOVE.L  D0,-(SP)        ;save the index
        BSR.L   SIZE_           ;get amount of free memory
        MOVE.L  (SP)+,D1        ;get back the index
        CMP.L   D1,D0           ;see if there's enough memory
        BLS.L   QSORRY          ;if not, say "Sorry"
        MOVE.L  VARBGN,D0       ;put address of array element...
        SUB.L   D1,D0           ;into D0
        RTS
TV1:
        CMP.B   #27,D0          ;if not @, is it A through Z?
        EOR     #1,CCR
        BCS     TSTVRT          ;if not, set Carry and return
        ADDQ    #1,A0   ;       else bump the text pointer
;
        CLR.L   D1
        MOVE.B  (a0),D1
        BSR             CVT26
        cmpi.b  #0xff,d1
        beq             tv2
        ADDQ    #1,A0   ; bump text pointer
        asl.l   #5,D1
        ADD.L   D1,D0
tv2:
        ADD     D0,D0           ;compute the variable's address
        ADD     D0,D0
        MOVE.L  VARBGN,D1
        ADD     D1,D0           ;and return it in D0 with Carry=0
TSTVRT:
        RTS

CVT26:
        cmpi.b  #'A',d1
        blo             CVT26a
        cmpi.b  #'Z',d1
        bhi             CVT26a
        subi.b  #'A',d1
        rts
CVT26a:
        moveq   #-1,d1
        rts
;*
;* ===== Multiplies the 32 bit values in D0 and D1, returning
;*      the 32 bit result in D0.
;*
MULT32:
        MOVE.L  D1,D4
        EOR.L   D0,D4   ;       see if the signs are the same
        TST.L   D0              ;take absolute value of D0
        BPL     MLT1
        NEG.L   D0
MLT1:
        TST.L   D1      ;       take absolute value of D1
        BPL     MLT2
        NEG.L   D1
MLT2:
        CMP.L   #0xFFFF,D1      ;is second argument <= 16 bits?
        BLS     MLT3    ;       OK, let it through
        EXG     D0,D1   ;       else swap the two arguments
        CMP.L   #0xFFFF,D1      ;and check 2nd argument again
        BHI.L   QHOW            ;one of them MUST be 16 bits
MLT3:
        MOVE    D0,D2   ;       prepare for 32 bit X 16 bit multiply
        MULU    D1,D2           ;multiply low word
        SWAP    D0
        MULU    D1,D0           ;multiply high word
        SWAP    D0
;*** Rick Murray's bug correction follows:
        TST     D0              ;if lower word not 0, then overflow
        BNE.L   QHOW    ;       if overflow, say "How?"
        ADD.L   D2,D0   ;       D0 now holds the product
        BMI.L   QHOW    ;       if sign bit set, it's an overflow
        TST.L   D4              ;were the signs the same?
        BPL     MLTRET
        NEG.L   D0              ;if not, make the result negative
MLTRET:
        RTS

;*
;* ===== Divide the 32 bit value in D0 by the 32 bit value in D1.
;*      Returns the 32 bit quotient in D0, remainder in D1.
;*
DIV32:
        TST.L   D1              ;check for divide-by-zero
        BEQ.L   QHOW            ;if so, say "How?"
        MOVE.L  D1,D2
        MOVE.L  D1,D4
        EOR.L   D0,D4           ;see if the signs are the same
        TST.L   D0              ;take absolute value of D0
        BPL     DIV1
        NEG.L   D0
DIV1:
        TST.L   D1      ;       take absolute value of D1
        BPL     DIV2
        NEG.L   D1
DIV2:
        MOVEQ   #31,D3  ;       iteration count for 32 bits
        MOVE.L  D0,D1
        CLR.L   D0
DIV3:
        ADD.L   D1,D1   ;       (This algorithm was translated from
        ADDX.L  D0,D0           ;the divide routine in Ron Cain's
        BEQ     DIV4            ;Small-C run time library.)
        CMP.L   D2,D0
        BMI     DIV4
        ADDQ.L  #1,D1
        SUB.L   D2,D0
DIV4:
        DBRA    D3,DIV3
        EXG     D0,D1   ;       put rem. & quot. in proper registers
        TST.L   D4      ;       were the signs the same?
        BPL     DIVRT
        NEG.L   D0      ;       if not, results are negative
        NEG.L   D1
DIVRT:
        RTS

;*
;* ===== The PEEK function returns the byte stored at the address
;*      contained in the following expression.
;*
PEEK:
        BSR     PARN    ;       get the memory address
        MOVE.L  D0,A1
        CLR.L   D0              ;upper 3 bytes will be zero
        MOVE.B  (A1),D0 ;       get the addressed byte
        RTS                     ;and return it

;*
;* ===== The RND function returns a random number from 1 to
;*      the value of the following expression in D0.
;*
RND:
        BSR     PARN    ;       get the upper limit
        TST.L   D0      ;       it must be positive and non-zero
        BEQ.L   QHOW
        BMI.L   QHOW
        
;       move.l  d0,-(a7)
;       move.l  RANPNT,D1
;       move.l  #16807,d0
;       bsr             MULT32
;       move.l  d0,RANPNT
;       move.l  (a7)+,d1
        MOVE.L  D0,D1
        MOVE.W  RANDOM+2,D0
        SWAP    D0
        MOVE.W  RANDOM,D0

;       MOVE.L  D0,D1
;       MOVE.L  RANPNT,A1       ;get memory as a random number
;       CMP.L   #LSTROM,A1
;       BCS     RA1
;       LEA     START,A1        ;wrap around if end of program
;RA1:
;       MOVE.L  (A1)+,D0;       get the slightly random number
        BCLR    #31,D0  ;       make sure it's positive
;       MOVE.L  A1,RANPNT       ;(even I can do better than this!)
        BSR     DIV32           ;RND(n)=MOD(number,n)+1
        MOVE.L  D1,D0   ;       MOD is the remainder of the div.
        ADDQ.L  #1,D0
        RTS

;*
;* ===== The ABS function returns an absolute value in D0.
;*
ABS:
        BSR     PARN            ;get the following expr.'s value
        TST.L   D0
        BPL     ABSRT
        NEG.L   D0              ;if negative, complement it
        BMI.L   QHOW    ;       if still negative, it was too big
ABSRT:
        RTS

;* RTF
;* ===== The SGN function returns the sign value in D0.
;*
SGN:
        BSR             PARN    ;get the following expr.'s value
        TST.L   D0
        BEQ             SGNRT
        BMI             SGNMI
        MOVEQ   #1,d0
SGNRT:
        RTS
SGNMI:
        MOVEQ   #-1,d0
        RTS

;*
;* ===== The SIZE function returns the size of free memory in D0.
;*
SIZE_:
        MOVE.L  VARBGN,D0       ;get the number of free bytes...
        SUB.L   TXTUNF,D0       ;between 'TXTUNF' and 'VARBGN'
        RTS                     ;return the number in D0

;* RTF
;* ===== return the millisecond time value
;*
TICK:
        move.l  Milliseconds,d0
        rts
        
TEMP:
        bsr             ReadTemp
        andi.l  #0xffff,d0
        rts

;*
;*******************************************************************
;*
;* *** SETVAL *** FIN *** ENDCHK *** ERROR (& friends) ***
;*
;* 'SETVAL' expects a variable, followed by an equal sign and then
;* an expression.  It evaluates the expression and sets the variable
;* to that value.
;*
;* 'FIN' checks the end of a command.  If it ended with ":",
;* execution continues. If it ended with a CR, it finds the
;* the next line and continues from there.
;*
;* 'ENDCHK' checks if a command is ended with a CR. This is
;* required in certain commands, such as GOTO, RETURN, STOP, etc.
;*
;* 'ERROR' prints the string pointed to by A0. It then prints the
;* line pointed to by CURRNT with a "?" inserted at where the
;* old text pointer (should be on top of the stack) points to.
;* Execution of Tiny BASIC is stopped and a warm start is done.
;* If CURRNT is zero (indicating a direct command), the direct
;* command is not printed. If CURRNT is -1 (indicating
;* 'INPUT' command in progress), the input line is not printed
;* and execution is not terminated but continues at 'INPERR'.
;*
;* Related to 'ERROR' are the following:
;* 'QWHAT' saves text pointer on stack and gets "What?" message.
;* 'AWHAT' just gets the "What?" message and jumps to 'ERROR'.
;* 'QSORRY' and 'ASORRY' do the same kind of thing.
;* 'QHOW' and 'AHOW' also do this for "How?".
;*
SETVAL:
        BSR     TSTV    ;       variable name?
        BCS     QWHAT           ;if not, say "What?"
        MOVE.L  D0,-(SP);       save the variable's address
        BSR.L   TSTC    ;       get past the "=" sign
        DC.B    '=',SV1-$
        BSR     EXPR    ;       evaluate the expression
        MOVE.L  (SP)+,A6
        MOVE.L  D0,(A6) ;       and save its value in the variable
        RTS
SV1:
        BRA     QWHAT   ;       if no "=" sign

FIN:
        BSR.L   TSTC    ;       *** FIN ***
        DC.B    ':',FI1-$
        ADDQ.L  #4,SP   ;       if ":", discard return address
        BRA     RUNSML  ;       continue on the same line
FI1:
        BSR.L   TSTC    ;       not ":", is it a CR?
        DC.B    CR,FI2-$
        ADDQ.L  #4,SP   ;       yes, purge return address
        BRA     RUNNXL          ;execute the next line
FI2:
        RTS                     ;else return to the caller

ENDCHK:
        BSR.L   IGNBLK
        CMP.B   #CR,(A0);       does it end with a CR?
        BNE     QWHAT   ;       if not, say "WHAT?"
        RTS

QWHAT:
        MOVE.L  A0,-(SP)
AWHAT:
        LEA     WHTMSG,A6
ERROR:
        BSR.L   PRMESG  ;       display the error message
        MOVE.L  (SP)+,A0        ;restore the text pointer
        MOVE.L  CURRNT,D0       ;get the current line number
        BEQ     WSTART          ;if zero, do a warm start
        CMP.L   #-1,D0          ;is the line no. pointer = -1?
        BEQ     INPERR          ;if so, redo input
        MOVE.B  (A0),-(SP)      ;save the char. pointed to
        CLR.B   (A0)            ;put a zero where the error is
        MOVE.L  CURRNT,A1       ;point to start of current line
        BSR.L   PRTLN           ;display the line in error up to the 0
        MOVE.B  (SP)+,(A0)      ;restore the character
        MOVE.B  #'?',D0     ;    display a "?"
        BSR     GOOUT
        CLR     D0
        SUBQ.L  #1,A1           ;point back to the error char.
        BSR.L   PRTSTG          ;display the rest of the line
        BRA     WSTART          ;and do a warm start
QSORRY:
        MOVE.L  A0,-(SP)
ASORRY:
        LEA     SRYMSG,A6
        BRA     ERROR
QHOW:
        MOVE.L  A0,-(SP)        ;Error: "How?"
AHOW:
        LEA     HOWMSG,A6
        BRA     ERROR
;*
;*******************************************************************
;*
;* *** GETLN *** FNDLN (& friends) ***
;*
;* 'GETLN' reads in input line into 'BUFFER'. It first prompts with
;* the character in D0 (given by the caller), then it fills the
;* buffer and echos. It ignores LF's but still echos
;* them back. Control-H is used to delete the last character
;* entered (if there is one), and control-X is used to delete the
;* whole line and start over again. CR signals the end of a line,
;* and causes 'GETLN' to return.
;*
GETLN:
        BSR     GOOUT           ;display the prompt
        MOVE.B  #' ',D0      ;   and a space
        BSR     GOOUT
        LEA     BUFFER,A0;      A0 is the buffer pointer
GL1:
        BSR.L   CHKIO;          check keyboard
        BEQ     GL1     ;       wait for a char. to come in
        CMP.B   #CTRLH,D0       ;delete last character?
        BEQ     GL3     ;       if so
        CMP.B   #CTRLX,D0;      delete the whole line?
        BEQ     GL4     ;       if so
        CMP.B   #CR,D0  ;       accept a CR
        BEQ     GL2
        CMP.B   #' ',D0  ;       if other control char., discard it
        BCS     GL1
GL2:
        MOVE.B  D0,(A0)+;       save the char.
        BSR     GOOUT           ;echo the char back out
        CMP.B   #CR,D0  ;       if it's a CR, end the line
        BEQ     GL7
        CMP.L   #(BUFFER+BUFLEN-1),A0   ;any more room?
        BCS     GL1     ;       yes: get some more, else delete last char.
GL3:
        MOVE.B  #CTRLH,D0       ;delete a char. if possible
        BSR     GOOUT
        MOVE.B  #' ',D0
        BSR     GOOUT
        CMP.L   #BUFFER,A0      ;any char.'s left?
        BLS     GL1             ;if not
        MOVE.B  #CTRLH,D0;      if so, finish the BS-space-BS sequence
        BSR     GOOUT
        SUBQ.L  #1,A0   ;       decrement the text pointer
        BRA     GL1             ;back for more
GL4:
        MOVE.L  A0,D1   ;       delete the whole line
        SUB.L   #BUFFER,D1;     figure out how many backspaces we need
        BEQ     GL6             ;if none needed, branch
        SUBQ    #1,D1   ;       adjust for DBRA
GL5:
        MOVE.B  #CTRLH,D0       ;and display BS-space-BS sequences
        BSR     GOOUT
        MOVE.B  #' ',D0
        BSR     GOOUT
        MOVE.B  #CTRLH,D0
        BSR     GOOUT
        DBRA    D1,GL5
GL6:
        LEA     BUFFER,A0       ;reinitialize the text pointer
        BRA     GL1             ;and go back for more
GL7:
        MOVE.B  #LF,D0  ;       echo a LF for the CR
        BSR     GOOUT
        RTS

;*
;*******************************************************************
;*
;* *** FNDLN (& friends) ***
;*
;* 'FNDLN' finds a line with a given line no. (in D1) in the
;* text save area.  A1 is used as the text pointer. If the line
;* is found, A1 will point to the beginning of that line
;* (i.e. the high byte of the line no.), and flags are NC & Z.
;* If that line is not there and a line with a higher line no.
;* is found, A1 points there and flags are NC & NZ. If we reached
;* the end of the text save area and cannot find the line, flags
;* are C & NZ.
;* 'FNDLN' will initialize A1 to the beginning of the text save
;* area to start the search. Some other entries of this routine
;* will not initialize A1 and do the search.
;* 'FNDLNP' will start with A1 and search for the line no.
;* 'FNDNXT' will bump A1 by 2, find a CR and then start search.
;* 'FNDSKP' uses A1 to find a CR, and then starts the search.
;*
FNDLN:
        CMP.L   #0xFFFF,D1      ;line no. must be < 65535
        BCC     QHOW
        MOVE.L  TXTBGN,A1       ;init. the text save pointer

FNDLNP:
        MOVE.L  TXTUNF,A2       ;check if we passed the end
        SUBQ.L  #1,A2
        CMPA.L  A1,A2
        BCS     FNDRET  ;       if so, return with Z=0 & C=1
        MOVE.B  (A1),D2 ;if not, get a line no.
        LSL.W   #8,D2
        MOVE.B  1(A1),D2
        CMP.W   D1,D2           ;is this the line we want?
        BCS     FNDNXT          ;no, not there yet
FNDRET:
        RTS                     ;return the cond. codes

FNDNXT:
        ADDQ.L  #2,A1;          find the next line

FNDSKP:
        CMP.B   #CR,(A1)+       ;try to find a CR
        BNE     FNDSKP          ;keep looking
        BRA     FNDLNP          ;check if end of text

;*
;*******************************************************************
;*
;* *** MVUP *** MVDOWN *** POPA *** PUSHA ***
;*
;* 'MVUP' moves a block up from where A1 points to where A2 points
;* until A1=A3
;*
;* 'MVDOWN' moves a block down from where A1 points to where A3
;* points until A1=A2
;*
;* 'POPA' restores the 'FOR' loop variable save area from the stack
;*
;* 'PUSHA' stacks for 'FOR' loop variable save area onto the stack
;*
MVUP:
        CMP.L   A1,A3   ;       see the above description
        BEQ     MVRET
        MOVE.B  (A1)+,(A2)+
        BRA     MVUP
MVRET:
        RTS

MVDOWN:
        CMP.L   A1,A2   ;       see the above description
        BEQ     MVRET
        MOVE.B  -(A1),-(A3)
        BRA     MVDOWN

POPA:
        MOVE.L  (SP)+,A6        ;A6 = return address
        MOVE.L  (SP)+,LOPVAR    ;restore LOPVAR, but zero means no more
        BEQ     PP1
        MOVE.L  (SP)+,LOPINC    ;if not zero, restore the rest
        MOVE.L  (SP)+,LOPLMT
        MOVE.L  (SP)+,LOPLN
        MOVE.L  (SP)+,LOPPT
PP1:
        JMP     (A6)    ;       return

PUSHA:
        MOVE.L  STKLMT,D1       ;Are we running out of stack room?
        SUB.L   SP,D1
        BCC             QSORRY          ;if so, say we're sorry
        MOVE.L  (SP)+,A6        ;else get the return address
        MOVE.L  LOPVAR,D1       ;save loop variables
        BEQ             PU1             ;if LOPVAR is zero, that's all
        MOVE.L  LOPPT,-(SP)     ;else save all the others
        MOVE.L  LOPLN,-(SP)
        MOVE.L  LOPLMT,-(SP)
        MOVE.L  LOPINC,-(SP)
PU1:
        MOVE.L  D1,-(SP)
        JMP     (A6)            ;return

;*
;*******************************************************************
;*
;* *** PRTSTG *** QTSTG *** PRTNUM *** PRTLN ***
;*
;* 'PRTSTG' prints a string pointed to by A1. It stops printing
;* and returns to the caller when either a CR is printed or when
;* the next byte is the same as what was passed in D0 by the
;* caller.
;*
;* 'QTSTG' looks for an underline (back-arrow on some systems),
;* single-quote, or double-quote.  If none of these are found, returns
;* to the caller.  If underline, outputs a CR without a LF.  If single
;* or double quote, prints the quoted string and demands a matching
;* end quote.  After the printing, the next 2 bytes of the caller are
;* skipped over (usually a short branch instruction).
;*
;* 'PRTNUM' prints the 32 bit number in D1, leading blanks are added if
;* needed to pad the number of spaces to the number in D4.
;* However, if the number of digits is larger than the no. in
;* D4, all digits are printed anyway. Negative sign is also
;* printed and counted in, positive sign is not.
;*
;* 'PRTLN' prints the saved text line pointed to by A1
;* with line no. and all.
;*
PRTSTG:
        MOVE.B  D0,D1   ;       save the stop character
PS1:
        MOVE.B  (A1)+,D0        ;get a text character
        CMP.B   D0,D1           ;same as stop character?
        BEQ             PRTRET          ;if so, return
        BSR             GOOUT           ;display the char.
        CMP.B   #CR,D0          ;;is it a C.R.?
        BNE             PS1             ;no, go back for more
        MOVE.B  #LF,D0  ;       yes, add a L.F.
        BSR             GOOUT
PRTRET:
        RTS                     ;then return

QTSTG:
        BSR.L   TSTC;           *** QTSTG ***
        DC.B    '"',QT3-$
        MOVE.B  #'"',D0  ;       it is a "
QT1:
        MOVE.L  A0,A1
        BSR     PRTSTG          ;print until another
        MOVE.L  A1,A0
        MOVE.L  (SP)+,A1;       pop return address
        CMP.B   #LF,D0  ;       was last one a CR?
        BEQ     RUNNXL          ;if so, run next line
QT2:
        ADDQ.L  #2,A1   ;       skip 2 bytes on return
        JMP     (A1)            ;return
QT3:
        BSR.L   TSTC    ;       is it a single quote?
        DC.B    '\'',QT4-$
        MOVE.B  #'''',D0  ;      if so, do same as above
        BRA     QT1
QT4:
        BSR.L   TSTC            ;is it an underline?
        DC.B    '_',QT5-$
        MOVE.B  #CR,D0          ;if so, output a CR without LF
        BSR.L   GOOUT
        MOVE.L  (SP)+,A1        ;pop return address
        BRA     QT2
QT5:
        RTS                     ;none of the above

PRTNUM:
        movem.l d0/d1/d4/a1/a5,-(a7)
        lea             scratch1,a5
        move.l  d1,d0
        jsr             HEX2DEC
        lea             scratch1,a5
PN8:
        move.b  (a5)+,d0
        beq             PN7
        dbra    d4,PN8
PN7:
        tst.w   d4
        bmi             PN9
        MOVE.B  #' ',D0  ;       display the required leading spaces
        BSR             GOOUT
        DBRA    D4,PN7
PN9:
        lea             scratch1,a1
        jsr             DisplayString
        movem.l (a7)+,d0/d1/d4/a1/a5
        rts

;PRTNUM 
;       MOVE.L  D1,D3   ;       save the number for later
;       MOVE.L  D4,-(SP)        ;save the width value
;       MOVE.W  #0xFFFF,-(SP)   ;flag for end of digit string
;       TST.L   D1              ;is it negative?
;       BPL     PN1             ;if not
;       NEG.L   D1      ;       else make it positive
;       SUBQ    #1,D4   ;       one less for width count
;PN1:
;       DIVU    #10,D1  ;       get the next digit
;       BVS     PNOV    ;       overflow flag set?
;       MOVE.L  D1,D0   ;       if not, save remainder
;       AND.L   #0xFFFF,D1      ;strip the remainder
;       BRA     TOASCII         ;skip the overflow stuff
;PNOV:
;       MOVE    D1,D0   ;       prepare for long word division
;       CLR.W   D1              ;zero out low word
;       SWAP    D1              ;high word into low
;       DIVU    #10,D1  ;       divide high word
;       MOVE    D1,D2   ;       save quotient
;       MOVE    D0,D1   ;       low word into low
;       DIVU    #10,D1  ;       divide low word
;       MOVE.L  D1,D0   ;       D0 = remainder
;       SWAP    D1              ;       R/Q becomes Q/R
;       MOVE    D2,D1   ;       D1 is low/high
;       SWAP    D1              ;       D1 is finally high/low
;TOASCII:
;       SWAP    D0              ;       get remainder
;       MOVE.W  D0,-(SP);       stack it as a digit
;       SWAP    D0
;       SUBQ    #1,D4   ;       decrement width count
;       TST.L   D1              ;if quotient is zero, we're done
;       BNE     PN1
;       SUBQ    #1,D4   ;       adjust padding count for DBRA
;       BMI     PN4             ;skip padding if not needed
;PN3:
;       MOVE.B  #' ',D0  ;       display the required leading spaces
;       BSR     GOOUT
;       DBRA    D4,PN3
;PN4:
;       TST.L   D3              ;is number negative?
;       BPL     PN5
;       MOVE.B  #'-',D0  ;       if so, display the sign
;       BSR     GOOUT
;PN5:
;       MOVE.W  (SP)+,D0        ;now unstack the digits and display
;       BMI     PNRET           ;until the flag code is reached
;       ADD.B   #'0',D0   ;      make into ASCII
;       BSR     GOOUT
;       BRA     PN5
;PNRET:
;       MOVE.L  (SP)+,D4        ;restore width value
;       RTS

PRTLN:
        CLR.L   D1
        MOVE.B  (A1)+,D1        ;get the binary line number
        LSL     #8,D1
        MOVE.B  (A1)+,D1
        MOVEQ   #5,D4           ;display a 5 digit line no.
        BSR     PRTNUM
        MOVE.B  #' ',D0      ;   followed by a blank
        BSR     GOOUT
        CLR     D0              ;stop char. is a zero
        BRA     PRTSTG  ;       display the rest of the line

;*
;* ===== Test text byte following the call to this subroutine. If it
;*      equals the byte pointed to by A0, return to the code following
;*      the call. If they are not equal, branch to the point
;*      indicated by the offset byte following the text byte.
;*
TSTC:
        BSR     IGNBLK          ;ignore leading blanks
        MOVE.L  (SP)+,A1        ;get the return address
        MOVE.B  (A1)+,D1        ;get the byte to compare
        CMP.B   (A0),D1         ;is it = to what A0 points to?
        BEQ     TC1             ;if so
        CLR.L   D1              ;If not, add the second
        MOVE.B  (A1),D1 ;       byte following the call to
        ADD.L   D1,A1   ;       the return address.
        JMP     (A1)            ;jump to the routine
TC1:
        ADDQ.L  #1,A0   ;       if equal, bump text pointer
        ADDQ.L  #1,A1   ;       Skip the 2 bytes following
        JMP     (A1)            ;the call and continue.

;*
;* ===== See if the text pointed to by A0 is a number. If so,
;*      return the number in D1 and the number of digits in D2,
;*      else return zero in D1 and D2.
;*
TSTNUM:
        CLR.L   D1              ;initialize return parameters
        CLR     D2
        BSR     IGNBLK          ;skip over blanks
TN1:
        CMP.B   #'0',(A0) ;      is it less than zero?
        BCS     TSNMRET         ;if so, that's all
        CMP.B   #'9',(A0) ;      is it greater than nine?
        BHI     TSNMRET         ;if so, return
        CMP.L   #214748364,D1   ;see if there's room for new digit
        BCC     QHOW            ;if not, we've overflowd
        MOVE.L  D1,D0   ;       quickly multiply result by 10
        ADD.L   D1,D1
        ADD.L   D1,D1
        ADD.L   D0,D1
        ADD.L   D1,D1
        MOVE.B  (A0)+,D0        ;add in the new digit
        AND.L   #0xF,D0
        ADD.L   D0,D1
        ADDQ    #1,D2           ;increment the no. of digits
        BRA     TN1
TSNMRET:
        RTS

;*
;* ===== Skip over blanks in the text pointed to by A0.
;*
IGNBLK:
        CMP.B   #' ',(A0)   ;    see if it's a space
        BNE     IGBRET          ;if so, swallow it
IGB1:
        ADDQ.L  #1,A0   ;       increment the text pointer
        BRA     IGNBLK
IGBRET:
        RTS

;*
;* ===== Convert the line of text in the input buffer to upper
;*      case (except for stuff between quotes).
;*
TOUPBUF:
        LEA     BUFFER,A0       ;set up text pointer
        CLR.B   D1              ;clear quote flag
TOUPB1:
        MOVE.B  (A0)+,D0        ;get the next text char.
        CMP.B   #CR,D0          ;is it end of line?
        BEQ     TOUPBRT         ;if so, return
        CMP.B   #'"',D0  ;       a double quote?
        BEQ     DOQUO
        CMP.B   #'''',D0  ;      or a single quote?
        BEQ     DOQUO
        TST.B   D1              ;inside quotes?
        BNE     TOUPB1          ;if so, do the next one
        BSR     TOUPPER         ;convert to upper case
        MOVE.B  D0,-(A0);       store it
        ADDQ.L  #1,A0
        BRA     TOUPB1          ;and go back for more
TOUPBRT:
        RTS

DOQUO:
        TST.B   D1      ;       are we inside quotes?
        BNE     DOQUO1
        MOVE.B  D0,D1   ;       if not, toggle inside-quotes flag
        BRA     TOUPB1
DOQUO1:
        CMP.B   D0,D1   ;       make sure we're ending proper quote
        BNE     TOUPB1          ;if not, ignore it
        CLR.B   D1              ;else clear quote flag
        BRA     TOUPB1

;*
;* ===== Convert the character in D0 to upper case
;*
TOUPPER:
        CMP.B   #'a',D0   ;      is it < 'a'?
        BCS     TOUPRET
        CMP.B   #'z',D0        ; or > 'z'?
        BHI     TOUPRET
        SUB.B   #32,D0          ;if not, make it upper case
TOUPRET:
        RTS

;*
;* 'CHKIO' checks the input. If there's no input, it will return
;* to the caller with the Z flag set. If there is input, the Z
;* flag is cleared and the input byte is in D0. However, if a
;* control-C is read, 'CHKIO' will warm-start BASIC and will not
;* return to the caller.
;*
CHKIO:
        BSR.L   GOIN    ;       get input if possible
        BEQ     CHKRET          ;if Zero, no input
        CMP.B   #CTRLC,D0       ;is it control-C?
        BNE     CHKRET          ;if not
        BRA.L   WSTART          ;if so, do a warm start
CHKRET:
        RTS

;*
;* ===== Display a CR-LF sequence
;*
CRLF1:
        LEA     CLMSG,A6

;*
;* ===== Display a zero-ended string pointed to by register A6
;*
PRMESG:
        MOVE.B  (A6)+,D0        ;get the char.
        BEQ     PRMRET          ;if it's zero, we're done
        BSR     GOOUT           ;else display it
        BRA     PRMESG
PRMRET:
        RTS

;******************************************************
;* The following routines are the only ones that need *
;* to be changed for a different I/O environment.     *
;******************************************************

;UART           EQU             0xFFDC0A00
;UART_LS                EQU             UART+1
;UART_CTRL      EQU             UART+7
;KEYBD          EQU             0xFFDC0000


;*
;* ===== Output character to the console (Port 1) from register D0
;*      (Preserves all registers.)
;*
OUTC:
        MOVEM.L D0/D1,-(SP)
        MOVE.L  D0,D1
        JSR             DisplayChar
        MOVEM.L (SP)+,D0/D1
        RTS

;*
;* ===== Input a character from the console into register D0 (or
;*      return Zero status if there's no character available).
;*
INC:
        MOVE.W  KEYBD,D0        ;is character ready?
        BPL             INCRET0         ;if not, return Zero status
        CLR.W   KEYBD+2         ; clear keyboard strobe line
        AND.W   #0xFF,D0        ;zero out the high bit
        RTS
INCRET0
        MOVEQ   #0,D0
        RTS

;*
;* ===== Output character to the host (Port 2) from register D0
;*      (Preserves all registers.)
;*
AUXOUT:
        BTST    #5,UART_LS      ;is port ready for a character?
        BEQ             AUXOUT          ;if not, wait for it
        MOVE.B  D0,UART         ;out it goes.
        RTS

;*
;* ===== Input a character from the host into register D0 (or
;*      return Zero status if there's no character available).
;*
AUXIN:
        BTST    #0,UART_LS      ;is character ready?
        BEQ             AXIRET          ;if not, return Zero status
        MOVE.B  UART,D0         ;else get the character
        AND.B   #0x7F,D0        ;zero out the high bit
AXIRET:
        RTS

;*
;* ===== Return to the resident monitor, operating system, etc.
;*
BYEBYE:
        JMP             Monitor
;    MOVE.B     #228,D7         ;return to Tutor
;       TRAP    #14

INITMSG:
        DC.B    CR,LF,'Gordo\'s MC68000 Tiny BASIC, v1.3',CR,LF,LF,0
OKMSG:
        DC.B    CR,LF,'OK',CR,LF,0
HOWMSG:
        DC.B    'How?',CR,LF,0
WHTMSG:
        DC.B    'What?',CR,LF,0
SRYMSG:
        DC.B    'Sorry.'
CLMSG:
        DC.B    CR,LF,0
;       DC.B    0        ;<- for aligning on a word boundary
        even
        
LSTROM  EQU             $
        ;       end of possible ROM area

;**************************************************************************
; The portion of code within STAR lines are modified from Tutor source code
;
;
; HEX2DEC   HEX2DEC convert hex to decimal                   
; CONVERT BINARY TO DECIMAL  REG D0 PUT IN (A5) BUFFER AS ASCII

HEX2DEC  MOVEM.L D1/D2/D3/D4/D5/D6/D7,-(SP)   ;SAVE REGISTERS
         MOVE.L  D0,D7               ;SAVE IT HERE
         BPL.S   HX2DC
         NEG.L   D7             ;CHANGE TO POSITIVE
         BMI.S   HX2DC57        ;SPECIAL CASE (-0)
         MOVE.B  #'-',(A5)+     ;PUT IN NEG SIGN
HX2DC    CLR.W   D4             ;FOR ZERO SURPRESS
         MOVEQ.L   #10,D6         ;COUNTER
HX2DC0   MOVEQ.L   #1,D2          ;VALUE TO SUB
         MOVE.L  D6,D1          ;COUNTER
         SUBQ.L  #1,D1          ;ADJUST - FORM POWER OF TEN
         BEQ.S   HX2DC2         ;IF POWER IS ZERO
HX2DC1   MOVE.W  D2,D3          ;D3=LOWER WORD
         MULU.W    #10,D3
         SWAP.W    D2             ;D2=UPPER WORD
         MULU.W    #10,D2
         SWAP.W    D3             ;ADD UPPER TO UPPER
         ADD.W   D3,D2
         SWAP.W    D2             ;PUT UPPER IN UPPER
         SWAP.W    D3             ;PUT LOWER IN LOWER
         MOVE.W  D3,D2          ;D2=UPPER & LOWER
         SUBQ.L  #1,D1
         BNE     HX2DC1
HX2DC2   CLR.L   D0             ;HOLDS SUB AMT
HX2DC22  CMP.L   D2,D7
         BLT.S   HX2DC3         ;IF NO MORE SUB POSSIBLE
         ADDQ.L  #1,D0          ;BUMP SUBS
         SUB.L   D2,D7          ;COUNT DOWN BY POWERS OF TEN
         BRA.S   HX2DC22        ;DO MORE
HX2DC3   TST.B   D0             ;ANY VALUE?
         BNE.S   HX2DC4
         TST.W   D4             ;ZERO SURPRESS
         BEQ.S   HX2DC5
HX2DC4   ADDI.B  #0x30,D0        ;BINARY TO ASCII
         MOVE.B  D0,(A5)+       ;PUT IN BUFFER
         MOVE.B  D0,D4          ;MARK AS NON ZERO SURPRESS
HX2DC5   SUBQ.L  #1,D6          ;NEXT POWER
         BNE     HX2DC0
         TST.W   D4             ;SEE IF ANYTHING PRINTED
         BNE.S   HX2DC6
HX2DC57  MOVE.B  #'0',(A5)+     ;PRINT AT LEST A ZERO
HX2DC6   MOVE.B  #0,(A5)        ; PUT TERMINATOR
         MOVEM.L (SP)+,D1/D2/D3/D4/D5/D6/D7   ;RESTORE REGISTERS
         RTS                    ;END OF ROUTINE

;******************************************************************************

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.