1608 lines
35 KiB
NASM
1608 lines
35 KiB
NASM
; Seattle Computer Products 8086 Monitor version 1.5 4/24/80
|
|
; by Tim Paterson
|
|
; This software is not copyrighted.
|
|
|
|
|
|
;To select a disk boot, set one of the following equates
|
|
;to 1, the rest to 0.
|
|
|
|
CROMEMCO4FDC: EQU 1 ;1 for 4FDC, 0 for others
|
|
NORTHSTARSD: EQU 0 ;North Star single density?
|
|
TARBELL: EQU 0 ;Tarbell (single or double)?
|
|
OTHER: EQU 0 ;User-defined disk
|
|
|
|
PUTBASE:EQU 100H
|
|
LOAD: EQU 200H
|
|
ORG 7F0H
|
|
PUT PUTBASE+7F0H
|
|
JMP 0,0FF80H ;Power-on jump to monitor
|
|
|
|
;Baud Rate Table. The 9513 divides 2MHz by these values.
|
|
;They are for 9600, 1200, 300, 150, 110 baud
|
|
|
|
BAUD: DW 13,104,416,832,1144
|
|
|
|
ORG 100H ;RAM area base address
|
|
|
|
;System Equates
|
|
|
|
BASE: EQU 0F0H ;CPU Support base port address
|
|
STAT: EQU BASE+7 ;UART status port
|
|
DATA: EQU BASE+6 ;UART data port
|
|
DAV: EQU 2 ;UART data available bit
|
|
TBMT: EQU 1 ;UART transmitter ready bit
|
|
BUFLEN: EQU 80 ;Maximum length of line input buffer
|
|
BPMAX: EQU 10 ;Maximum number of breakpoints
|
|
BPLEN: EQU BPMAX+BPMAX ;Length of breakpoint table
|
|
REGTABLEN:EQU 14 ;Number of registers
|
|
SEGDIF: EQU 800H ;-0FF800H (ROM address)
|
|
PROMPT: EQU ">"
|
|
CAN: EQU "@"
|
|
|
|
;RAM area.
|
|
|
|
BRKCNT: DS 2 ;Number of breakpoints
|
|
TCOUNT: DS 2 ;Number of steps to trace
|
|
BPTAB: DS BPLEN ;Breakpoint table
|
|
LINEBUF:DS BUFLEN+1 ;Line input buffer
|
|
ALIGN
|
|
DS 50 ;Working stack area
|
|
STACK:
|
|
|
|
;Register save area
|
|
|
|
AXSAVE: DS 2
|
|
BXSAVE: DS 2
|
|
CXSAVE: DS 2
|
|
DXSAVE: DS 2
|
|
SPSAVE: DS 2
|
|
BPSAVE: DS 2
|
|
SISAVE: DS 2
|
|
DISAVE: DS 2
|
|
DSSAVE: DS 2
|
|
ESSAVE: DS 2
|
|
RSTACK: ;Stack set here so registers can be saved by pushing
|
|
SSSAVE: DS 2
|
|
CSSAVE: DS 2
|
|
IPSAVE: DS 2
|
|
FSAVE: DS 2
|
|
|
|
;Start of Monitor code
|
|
|
|
ORG 0
|
|
PUT PUTBASE
|
|
|
|
;One-time initialization
|
|
|
|
UP
|
|
XOR AX,AX
|
|
MOV SS,AX
|
|
MOV DS,AX
|
|
MOV ES,AX
|
|
MOV DI,AXSAVE
|
|
MOV CX,14
|
|
REP
|
|
STOW ;Set register images to zero
|
|
OR B,[FSAVE+1],2 ;Enable interrupts
|
|
MOV CL,4
|
|
MOV AL,40H
|
|
MOV DI,DSSAVE
|
|
REP
|
|
STOW ;Set segment reg. images to 40H
|
|
MOV B [SPSAVE+1],0CH ;Set user stack to 400H+0C00H
|
|
MOV SP,STACK
|
|
;Prepare 9513
|
|
MOV AL,17H
|
|
OUT BASE+5 ;Select Master Mode register
|
|
MOV AL,0F3H
|
|
OUT BASE+4 ;Low byte of Master Mode
|
|
MOV AX,584H ;Output 84H to BASE+4
|
|
OUTW BASE+4 ;and 05H to BASE+5
|
|
;Master Mode now set to 84F3H:
|
|
; Scaler set to BCD division
|
|
; Enable data pointer increment
|
|
; 8-bit data bus
|
|
; FOUT=100Hz, dividing F5 by 4 (F5=4MHz/10000)
|
|
; Both alarm comparators disabled
|
|
; Time-of-day enabled
|
|
;Counter 5 selected
|
|
|
|
;Initialize loop. Ports BASE through BASE+7 are initialized
|
|
;from table. Each table entry has number of bytes followed by
|
|
;data.
|
|
|
|
MOV SI,INITTABLE ;Initialization table
|
|
MOV DX,BASE ;DX has (variable) port no.
|
|
INITPORT:
|
|
SEG CS
|
|
LODB ;Get byte count
|
|
MOV CL,AL
|
|
JCXZ NEXTPORT ;No init. for some ports
|
|
INITBYTE:
|
|
SEG CS
|
|
LODB ;Get init. data
|
|
OUT DX ;Send to port
|
|
LOOP INITBYTE ;As many bytes as required
|
|
NEXTPORT:
|
|
INC DX ;Prepare for next port
|
|
CMP DL,BASE+8 ;Check against limit
|
|
JNZ INITPORT
|
|
|
|
;Initialization complete except for determining baud rate.
|
|
;Both 8259As are ready to accept interrupts, the 9513 is
|
|
;providing 19.2k baud X 16 to the 8251A which is set for
|
|
;16X clock and one stop bit.
|
|
|
|
CALL CHECKB ;Check for correct baud rate
|
|
;CHECKB does not return if baud rate is correct
|
|
|
|
;Intial baud rate (19.2k) was wrong, so run auto-baud routine
|
|
|
|
INITBAUD:
|
|
MOV SI,BAUD
|
|
;First set up 9513 for slower baud rates (<=9600).
|
|
;Counter 5 mode register has already been selected.
|
|
MOV AX,0E823H ;Output 23H to BASE+4
|
|
OUTW BASE+4 ;and 0E8H to BASE+5
|
|
;23H to BASE+4 sets lower half of Counter 5 mode register.
|
|
;Reload from Load, count down repetively in binary,
|
|
;toggle output.
|
|
;0E8H to BASE+5 disables data pointer sequencing
|
|
|
|
MOV AL,0DH
|
|
OUT BASE+5 ;Select Counter 5 load reg.
|
|
INITB:
|
|
SEG CS
|
|
LODW ;Get divisor
|
|
OUT BASE+4 ;Output low byte
|
|
MOV AL,AH
|
|
OUT BASE+4 ;Output high byte
|
|
CALL CHECKB ;Check if baud rate correct
|
|
JP INITB ;Try next rate if not
|
|
CHECKB:
|
|
CALL IN ;First byte could be messed up
|
|
CALL IN ;Get carriage return
|
|
CMP AL,13 ;Correct?
|
|
JZ MONITOR ;Don't return if correct
|
|
RET ;Didn't get it yet
|
|
|
|
;Initialization complete, including baud rate.
|
|
|
|
MONITOR:
|
|
; Do auto boot if sense switch 0 is on.
|
|
MOV DI,LINEBUF
|
|
MOV B,[DI],13 ;No breakpoints after boot
|
|
IN BASE+0FH ;Sense switch port
|
|
TEST AL,1
|
|
JZ DOMON
|
|
JMP BOOT
|
|
DOMON:
|
|
MOV SI,HEADER
|
|
CALL PRINTMES
|
|
COMMAND:
|
|
;Re-establish initial conditions
|
|
UP
|
|
XOR AX,AX
|
|
MOV DS,AX
|
|
MOV ES,AX
|
|
MOV SP,STACK
|
|
MOV [64H],INT ;Set UART interrupt vector
|
|
MOV [66H],CS
|
|
MOV AL,PROMPT
|
|
CALL OUT
|
|
CALL INBUF ;Get command line
|
|
;From now and throughout command line processing, DI points
|
|
;to next character in command line to be processed.
|
|
CALL SCANB ;Scan off leading blanks
|
|
JZ COMMAND ;Null command?
|
|
MOV AL,[DI] ;AL=first non-blank character
|
|
;Prepare command letter for table lookup
|
|
SUB AL,"B" ;Low end range check
|
|
JC ERR1
|
|
CMP AL,"T"+1-"B" ;Upper end range check
|
|
JNC ERR1
|
|
INC DI
|
|
SHL AL ;Times two
|
|
CBW ;Now a 16-bit quantity
|
|
XCHG BX,AX ;In BX we can address with it
|
|
SEG CS
|
|
CALL [BX+COMTAB] ;Execute command
|
|
JP COMMAND ;Get next command
|
|
ERR1: JMP ERROR
|
|
|
|
;Get input line
|
|
|
|
INBUF:
|
|
MOV DI,LINEBUF ;Next empty buffer location
|
|
XOR CX,CX ;Character count
|
|
GETCH:
|
|
CALL IN ;Get input character
|
|
CMP AL,20H ;Check for control characters
|
|
JC CONTROL
|
|
CMP AL,7FH ;RUBOUT is a backspace
|
|
JZ BACKSP
|
|
CALL OUT ;Echo character
|
|
CMP AL,CAN ;Cancel line?
|
|
JZ KILL
|
|
STOB ;Put in input buffer
|
|
INC CX ;Bump character count
|
|
CMP CX,BUFLEN ;Buffer full?
|
|
JBE GETCH ;Drop in to backspace if full
|
|
BACKSP:
|
|
JCXZ GETCH ;Can't backspace over nothing
|
|
DEC DI ;Drop pointer
|
|
DEC CX ;and character count
|
|
CALL BACKUP ;Send physical backspace
|
|
JP GETCH ;Get next char.
|
|
CONTROL:
|
|
CMP AL,8 ;Check for backspace
|
|
JZ BACKSP
|
|
CMP AL,13 ;Check for carriage return
|
|
JNZ GETCH ;Ignore all other control char.
|
|
STOB ;Put the car. ret. in buffer
|
|
MOV DI,LINEBUF ;Set up DI for command processing
|
|
|
|
;Output CR/LF sequence
|
|
|
|
CRLF:
|
|
MOV AL,13
|
|
CALL OUT
|
|
MOV AL,10
|
|
JP OUT
|
|
|
|
;Cancel input line
|
|
|
|
KILL:
|
|
CALL CRLF
|
|
JP COMMAND
|
|
|
|
;Character input routine
|
|
|
|
IN:
|
|
DI ;Poll, don't interrupt
|
|
INB STAT
|
|
TEST AL,DAV
|
|
JZ IN ;Loop until ready
|
|
INB DATA
|
|
AND AL,7FH ;Only 7 bits
|
|
EI ;Interrupts OK now
|
|
RET
|
|
|
|
;Physical backspace - blank, backspace, blank
|
|
|
|
BACKUP:
|
|
MOV SI,BACMES
|
|
|
|
;Print ASCII message. Last char has bit 7 set
|
|
|
|
PRINTMES:
|
|
SEG CS
|
|
LODB ;Get char to print
|
|
CALL OUT
|
|
SHL AL ;High bit set?
|
|
JNC PRINTMES
|
|
RET
|
|
|
|
;Scan for parameters of a command
|
|
|
|
SCANP:
|
|
CALL SCANB ;Get first non-blank
|
|
CMP B,[DI],"," ;One comma between params OK
|
|
JNE EOLCHK ;If not comma, we found param
|
|
INC DI ;Skip over comma
|
|
|
|
;Scan command line for next non-blank character
|
|
|
|
SCANB:
|
|
MOV AL," "
|
|
PUSH CX ;Don't disturb CX
|
|
MOV CL,-1 ;but scan as many as necessary
|
|
REPE
|
|
SCAB
|
|
DEC DI ;Back up to first non-blank
|
|
POP CX
|
|
EOLCHK:
|
|
CMP B,[DI],13
|
|
RET
|
|
|
|
;Print the 5-digit hex address of SI and DS
|
|
|
|
OUTSI:
|
|
MOV DX,DS ;Put DS where we can work with it
|
|
MOV AH,0 ;Will become high bits of DS
|
|
CALL SHIFT4 ;Shift DS four bits
|
|
ADD DX,SI ;Compute absolute address
|
|
JP OUTADD ;Finish below
|
|
|
|
;Print 5-digit hex address of DI and ES
|
|
;Same as OUTSI above
|
|
|
|
OUTDI:
|
|
MOV DX,ES
|
|
MOV AH,0
|
|
CALL SHIFT4
|
|
ADD DX,DI
|
|
;Finish OUTSI here too
|
|
OUTADD:
|
|
ADC AH,0 ;Add in carry to high bits
|
|
CALL HIDIG ;Output hex value in AH
|
|
|
|
;Print out 16-bit value in DX in hex
|
|
|
|
OUT16:
|
|
MOV AL,DH ;High-order byte first
|
|
CALL HEX
|
|
MOV AL,DL ;Then low-order byte
|
|
|
|
;Output byte in AL as two hex digits
|
|
|
|
HEX:
|
|
MOV AH,AL ;Save for second digit
|
|
;Shift high digit into low 4 bits
|
|
PUSH CX
|
|
MOV CL,4
|
|
SHR AL,CL
|
|
POP CX
|
|
|
|
CALL DIGIT ;Output first digit
|
|
HIDIG:
|
|
MOV AL,AH ;Now do digit saved in AH
|
|
DIGIT:
|
|
AND AL,0FH ;Mask to 4 bits
|
|
;Trick 6-byte hex conversion works on 8086 too.
|
|
ADD AL,90H
|
|
DAA
|
|
ADC AL,40H
|
|
DAA
|
|
|
|
;Console output of character in AL
|
|
|
|
OUT:
|
|
PUSH AX ;Character to output on stack
|
|
OUT1:
|
|
INB STAT
|
|
AND AL,TBMT
|
|
JZ OUT1 ;Wait until ready
|
|
POP AX
|
|
OUTB DATA
|
|
RET
|
|
|
|
;Output one space
|
|
|
|
BLANK:
|
|
MOV AL," "
|
|
JP OUT
|
|
|
|
;Output the number of blanks in CX
|
|
|
|
TAB:
|
|
CALL BLANK
|
|
LOOP TAB
|
|
RET
|
|
|
|
;Command Table. Command letter indexes into table to get
|
|
;address of command. PERR prints error for no such command.
|
|
|
|
COMTAB:
|
|
DW BOOT ;B
|
|
DW PERR ;C
|
|
DW DUMP ;D
|
|
DW ENTER ;E
|
|
DW FILL ;F
|
|
DW GO ;G
|
|
DW PERR ;H
|
|
DW INPUT ;I
|
|
DW PERR ;J
|
|
DW PERR ;K
|
|
DW PERR ;L
|
|
DW MOVE ;M
|
|
DW PERR ;N
|
|
DW OUTPUT ;O
|
|
DW PERR ;P
|
|
DW PERR ;Q
|
|
DW REG ;R
|
|
DW SEARCH ;S
|
|
DW TRACE ;T
|
|
|
|
;Given 20-bit address in AH:DX, breaks it down to a segment
|
|
;number in AX and a displacement in DX. Displacement is
|
|
;always zero except for least significant 4 bits.
|
|
|
|
GETSEG:
|
|
MOV AL,DL
|
|
AND AL,0FH ;AL has least significant 4 bits
|
|
CALL SHIFT4 ;4-bit left shift of AH:DX
|
|
MOV DL,AL ;Restore lowest 4 bits
|
|
MOV AL,DH ;Low byte of segment number
|
|
XOR DH,DH ;Zero high byte of displacement
|
|
RET
|
|
|
|
;Shift AH:DX left 4 bits
|
|
|
|
SHIFT4:
|
|
SHL DX
|
|
RCL AH ;1
|
|
SHL DX
|
|
RCL AH ;2
|
|
SHL DX
|
|
RCL AH ;3
|
|
SHL DX
|
|
RCL AH ;4
|
|
RET2: RET
|
|
|
|
;RANGE - Looks for parameters defining an address range.
|
|
;The first parameter is a hex number of 5 or less digits
|
|
;which specifies the starting address. The second parameter
|
|
;may specify the ending address, or it may be preceded by
|
|
;"L" and specify a length (4 digits max), or it may be
|
|
;omitted and a length of 128 bytes is assumed. Returns with
|
|
;segment no. in AX and displacement (0-F) in DX.
|
|
|
|
RANGE:
|
|
MOV CX,5 ;5 digits max
|
|
CALL GETHEX ;Get hex number
|
|
PUSH AX ;Save high 4 bits
|
|
PUSH DX ;Save low 16 bits
|
|
CALL SCANP ;Get to next parameter
|
|
CMP B,[DI],"L" ;Length indicator?
|
|
JE GETLEN
|
|
MOV DX,128 ;Default length
|
|
CALL HEXIN ;Second parameter present?
|
|
JC RNGRET ;If not, use default
|
|
MOV CX,5 ;5 hex digits
|
|
CALL GETHEX ;Get ending address
|
|
MOV CX,DX ;Low 16 bits of ending addr.
|
|
POP DX ;Low 16 bits of starting addr.
|
|
POP BX ;BH=hi 4 bits of start addr.
|
|
SUB CX,DX ;Compute range
|
|
SBB AH,BH ;Finish 20-bit subtract
|
|
JNZ RNGERR ;Range must be less than 64K
|
|
XCHG AX,BX ;AH=starting, BH=ending hi 4 bits
|
|
INC CX ;Range must include ending location
|
|
JP RNGCHK ;Finish range testing and return
|
|
GETLEN:
|
|
INC DI ;Skip over "L" to length
|
|
MOV CX,4 ;Length may have 4 digits
|
|
CALL GETHEX ;Get the range
|
|
RNGRET:
|
|
MOV CX,DX ;Length
|
|
POP DX ;Low 16 bits of starting addr.
|
|
POP AX ;AH=hi 4 bits of starting addr.
|
|
|
|
;RNGCHK verifies that the range lies entirely within one segment.
|
|
;CX=0 means count=10000H. Range is within one segment only if
|
|
;adding the low 4 bits of the starting address to the count is
|
|
;<=10000H, because segments can start only on 16-byte boundaries.
|
|
|
|
RNGCHK:
|
|
MOV BX,DX ;Low 16 bits of start addr.
|
|
AND BX,0FH ;Low 4 bits of starting addr.
|
|
JCXZ MAXRNG ;If count=10000H then BX must be 0
|
|
ADD BX,CX ;Must be <=10000H
|
|
JNC GETSEG ;OK if strictly <
|
|
MAXRNG:
|
|
;If here because of JCXZ MAXRNG, we are testing if low 4 bits
|
|
;(in BX) are zero. If we dropped straight in, we are testing
|
|
;for BX+CX=10000H (=0). Either way, zero flag set means
|
|
;withing range.
|
|
JZ GETSEG
|
|
RNGERR:
|
|
MOV AX,4700H+"R" ;RG ERROR
|
|
JMP ERR
|
|
|
|
;Dump an area of memory in both hex and ASCII
|
|
|
|
DUMP:
|
|
CALL RANGE ;Get range to dump
|
|
PUSH AX ;Save segment
|
|
CALL GETEOL ;Check for errors
|
|
POP DS ;Set segment
|
|
MOV SI,DX ;SI has displacement in segment
|
|
ROW:
|
|
CALL OUTSI ;Print address at start of line
|
|
PUSH SI ;Save address for ASCII dump
|
|
BYTE:
|
|
CALL BLANK ;Space between bytes
|
|
BYTE1:
|
|
LODB ;Get byte to dump
|
|
CALL HEX ;and display it
|
|
POP DX ;DX has start addr. for ASCII dump
|
|
DEC CX ;Drop loop count
|
|
JZ ASCII ;If through do ASCII dump
|
|
MOV AX,SI
|
|
TEST AL,0FH ;On 16-byte boundary?
|
|
JZ ENDROW
|
|
PUSH DX ;Didn't need ASCII addr. yet
|
|
TEST AL,7 ;On 8-byte boundary?
|
|
JNZ BYTE
|
|
MOV AL,"-" ;Mark every 8 bytes
|
|
CALL OUT
|
|
JP BYTE1
|
|
ENDROW:
|
|
CALL ASCII ;Show it in ASCII
|
|
JP ROW ;Loop until count is zero
|
|
ASCII:
|
|
PUSH CX ;Save byte count
|
|
MOV AX,SI ;Current dump address
|
|
MOV SI,DX ;ASCII dump address
|
|
SUB AX,DX ;AX=length of ASCII dump
|
|
;Compute tab length. ASCII dump always appears on right side
|
|
;screen regardless of how many bytes were dumped. Figure 3
|
|
;characters for each byte dumped and subtract from 51, which
|
|
;allows a minimum of 3 blanks after the last byte dumped.
|
|
MOV BX,AX
|
|
SHL AX ;Length times 2
|
|
ADD AX,BX ;Length times 3
|
|
MOV CX,51
|
|
SUB CX,AX ;Amount to tab in CX
|
|
CALL TAB
|
|
MOV CX,BX ;ASCII dump length back in CX
|
|
ASCDMP:
|
|
LODB ;Get ASCII byte to dump
|
|
AND AL,7FH ;ASCII uses 7 bits
|
|
CMP AL,7FH ;Don't try to print RUBOUT
|
|
JZ NOPRT
|
|
CMP AL," " ;Check for control characters
|
|
JNC PRIN
|
|
NOPRT:
|
|
MOV AL,"." ;If unprintable character
|
|
PRIN:
|
|
CALL OUT ;Print ASCII character
|
|
LOOP ASCDMP ;CX times
|
|
POP CX ;Restore overall dump length
|
|
JMP CRLF ;Print CR/LF and return
|
|
|
|
;Block move one area of memory to another. Overlapping moves
|
|
;are performed correctly, i.e., so that a source byte is not
|
|
;overwritten until after it has been moved.
|
|
|
|
MOVE:
|
|
CALL RANGE ;Get range of source area
|
|
PUSH CX ;Save length
|
|
PUSH AX ;Save segment
|
|
MOV SI,DX ;Set source displacement
|
|
MOV CX,5 ;Allow 5 digits
|
|
CALL GETHEX ;in destination address
|
|
CALL GETEOL ;Check for errors
|
|
CALL GETSEG ;Convert dest. to seg/disp
|
|
MOV DI,DX ;Set dest. displacement
|
|
POP BX ;Source segment
|
|
MOV DS,BX
|
|
MOV ES,AX ;Destination segment
|
|
POP CX ;Length
|
|
CMP DI,SI ;Check direction of move
|
|
SBB AX,BX ;Extend the CMP to 32 bits
|
|
JB COPYLIST ;Move forward into lower mem.
|
|
;Otherwise, move backward. Figure end of source and destination
|
|
;areas and flip direction flag.
|
|
DEC CX
|
|
ADD SI,CX ;End of source area
|
|
ADD DI,CX ;End of destination area
|
|
DOWN ;Reverse direction
|
|
INC CX
|
|
COPYLIST:
|
|
MOVB ;Do at least 1 - Range is 1-10000H not 0-FFFFH
|
|
DEC CX
|
|
REP
|
|
MOVB ;Block move
|
|
RET
|
|
|
|
;Fill an area of memory with a list values. If the list
|
|
;is bigger than the area, don't use the whole list. If the
|
|
;list is smaller, repeat it as many times as necessary.
|
|
|
|
FILL:
|
|
CALL RANGE ;Get range to fill
|
|
PUSH CX ;Save length
|
|
PUSH AX ;Save segment number
|
|
PUSH DX ;Save displacement
|
|
CALL LIST ;Get list of values to fill with
|
|
POP DI ;Displacement in segment
|
|
POP ES ;Segment
|
|
POP CX ;Length
|
|
CMP BX,CX ;BX is length of fill list
|
|
MOV SI,LINEBUF ;List is in line buffer
|
|
JCXZ BIGRNG
|
|
JAE COPYLIST ;If list is big, copy part of it
|
|
BIGRNG:
|
|
SUB CX,BX ;How much bigger is area than list?
|
|
XCHG CX,BX ;CX=length of list
|
|
PUSH DI ;Save starting addr. of area
|
|
REP
|
|
MOVB ;Move list into area
|
|
POP SI
|
|
;The list has been copied into the beginning of the
|
|
;specified area of memory. SI is the first address
|
|
;of that area, DI is the end of the copy of the list
|
|
;plus one, which is where the list will begin to repeat.
|
|
;All we need to do now is copy [SI] to [DI] until the
|
|
;end of the memory area is reached. This will cause the
|
|
;list to repeat as many times as necessary.
|
|
MOV CX,BX ;Length of area minus list
|
|
PUSH ES ;Different index register
|
|
POP DS ;requires different segment reg.
|
|
JP COPYLIST ;Do the block move
|
|
|
|
;Search a specified area of memory for given list of bytes.
|
|
;Print address of first byte of each match.
|
|
|
|
SEARCH:
|
|
CALL RANGE ;Get area to be searched
|
|
PUSH CX ;Save count
|
|
PUSH AX ;Save segment number
|
|
PUSH DX ;Save displacement
|
|
CALL LIST ;Get search list
|
|
DEC BX ;No. of bytes in list-1
|
|
POP DI ;Displacement within segment
|
|
POP ES ;Segment
|
|
POP CX ;Length to be searched
|
|
SUB CX,BX ; minus length of list
|
|
SCAN:
|
|
MOV SI,LINEBUF ;List kept in line buffer
|
|
LODB ;Bring first byte into AL
|
|
DOSCAN:
|
|
SCAB ;Search for first byte
|
|
LOOPNE DOSCAN ;Do at least once by using LOOP
|
|
JNZ RET ;Exit if not found
|
|
PUSH BX ;Length of list minus 1
|
|
XCHG BX,CX
|
|
PUSH DI ;Will resume search here
|
|
REPE
|
|
CMPB ;Compare rest of string
|
|
MOV CX,BX ;Area length back in CX
|
|
POP DI ;Next search location
|
|
POP BX ;Restore list length
|
|
JNZ TEST ;Continue search if no match
|
|
DEC DI ;Match address
|
|
CALL OUTDI ;Print it
|
|
INC DI ;Restore search address
|
|
CALL CRLF
|
|
TEST:
|
|
JCXZ RET
|
|
JP SCAN ;Look for next occurrence
|
|
|
|
;Get the next parameter, which must be a hex number.
|
|
;CX is maximum number of digits the number may have.
|
|
|
|
GETHEX:
|
|
CALL SCANP ;Scan to next parameter
|
|
GETHEX1:
|
|
XOR DX,DX ;Initialize the number
|
|
MOV AH,DH
|
|
CALL HEXIN ;Get a hex digit
|
|
JC ERROR ;Must be one valid digit
|
|
MOV DL,AL ;First 4 bits in position
|
|
GETLP:
|
|
INC DI ;Next char in buffer
|
|
DEC CX ;Digit count
|
|
CALL HEXIN ;Get another hex digit?
|
|
JC RET ;All done if no more digits
|
|
JCXZ ERROR ;Too many digits?
|
|
CALL SHIFT4 ;Multiply by 16
|
|
OR DL,AL ;and combine new digit
|
|
JP GETLP ;Get more digits
|
|
|
|
;Check if next character in the input buffer is a hex digit
|
|
;and convert it to binary if it is. Carry set if not.
|
|
|
|
HEXIN:
|
|
MOV AL,[DI]
|
|
|
|
;Check if AL has a hex digit and convert it to binary if it
|
|
;is. Carry set if not.
|
|
|
|
HEXCHK:
|
|
SUB AL,"0" ;Kill ASCII numeric bias
|
|
JC RET
|
|
CMP AL,10
|
|
CMC
|
|
JNC RET ;OK if 0-9
|
|
SUB AL,7 ;Kill A-F bias
|
|
CMP AL,10
|
|
JC RET
|
|
CMP AL,16
|
|
CMC
|
|
RET: RET
|
|
|
|
;Process one parameter when a list of bytes is
|
|
;required. Carry set if parameter bad. Called by LIST
|
|
|
|
LISTITEM:
|
|
CALL SCANP ;Scan to parameter
|
|
CALL HEXIN ;Is it in hex?
|
|
JC STRINGCHK ;If not, could be a string
|
|
MOV CX,2 ;Only 2 hex digits for bytes
|
|
CALL GETHEX ;Get the byte value
|
|
MOV [BX],DL ;Add to list
|
|
INC BX
|
|
GRET: CLC ;Parameter was OK
|
|
RET
|
|
STRINGCHK:
|
|
MOV AL,[DI] ;Get first character of param
|
|
CMP AL,"'" ;String?
|
|
JZ STRING
|
|
CMP AL,'"' ;Either quote is all right
|
|
JZ STRING
|
|
STC ;Not string, not hex - bad
|
|
RET
|
|
STRING:
|
|
MOV AH,AL ;Save for closing quote
|
|
INC DI
|
|
STRNGLP:
|
|
MOV AL,[DI] ;Next char of string
|
|
INC DI
|
|
CMP AL,13 ;Check for end of line
|
|
JZ ERROR ;Must find a close quote
|
|
CMP AL,AH ;Check for close quote
|
|
JNZ STOSTRG ;Add new character to list
|
|
CMP AH,[DI] ;Two quotes in a row?
|
|
JNZ GRET ;If not, we're done
|
|
INC DI ;Yes - skip second one
|
|
STOSTRG:
|
|
MOV [BX],AL ;Put new char in list
|
|
INC BX
|
|
JP STRNGLP ;Get more characters
|
|
|
|
;Get a byte list for ENTER, FILL or SEARCH. Accepts any number
|
|
;of 2-digit hex values or character strings in either single
|
|
;(') or double (") quotes.
|
|
|
|
LIST:
|
|
MOV BX,LINEBUF ;Put byte list in the line buffer
|
|
LISTLP:
|
|
CALL LISTITEM ;Process a parameter
|
|
JNC LISTLP ;If OK, try for more
|
|
SUB BX,LINEBUF ;BX now has no. of bytes in list
|
|
JZ ERROR ;List must not be empty
|
|
|
|
;Make sure there is nothing more on the line except for
|
|
;blanks and carriage return. If there is, it is an
|
|
;unrecognized parameter and an error.
|
|
|
|
GETEOL:
|
|
CALL SCANB ;Skip blanks
|
|
JNZ ERROR ;Better be a RETURN
|
|
RET
|
|
|
|
;Command error. DI has been incremented beyond the
|
|
;command letter so it must decremented for the
|
|
;error pointer to work.
|
|
|
|
PERR:
|
|
DEC DI
|
|
|
|
;Syntax error. DI points to character in the input buffer
|
|
;which caused error. By subtracting from start of buffer,
|
|
;we will know how far to tab over to appear directly below
|
|
;it on the terminal. Then print "^ Error".
|
|
|
|
ERROR:
|
|
SUB DI,LINEBUF-1 ;How many char processed so far?
|
|
MOV CX,DI ;Parameter for TAB in CX
|
|
CALL TAB ;Directly below bad char
|
|
MOV SI,SYNERR ;Error message
|
|
|
|
;Print error message and abort to command level
|
|
|
|
PRINT:
|
|
CALL PRINTMES
|
|
JMP COMMAND
|
|
|
|
;Short form of ENTER command. A list of values from the
|
|
;command line are put into memory without using normal
|
|
;ENTER mode.
|
|
|
|
GETLIST:
|
|
CALL LIST ;Get the bytes to enter
|
|
POP DI ;Displacement within segment
|
|
POP ES ;Segment to enter into
|
|
MOV SI,LINEBUF ;List of bytes is in line buffer
|
|
MOV CX,BX ;Count of bytes
|
|
REP
|
|
MOVB ;Enter that byte list
|
|
RET
|
|
|
|
;Enter values into memory at a specified address. If the
|
|
;line contains nothing but the address we go into "enter
|
|
;mode", where the address and its current value are printed
|
|
;and the user may change it if desired. To change, type in
|
|
;new value in hex. Backspace works to correct errors. If
|
|
;an illegal hex digit or too many digits are typed, the
|
|
;bell is sounded but it is otherwise ignored. To go to the
|
|
;next byte (with or without change), hit space bar. To
|
|
;back up to a previous address, type "-". On
|
|
;every 8-byte boundary a new line is started and the address
|
|
;is printed. To terminate command, type carriage return.
|
|
; Alternatively, the list of bytes to be entered may be
|
|
;included on the original command line immediately following
|
|
;the address. This is in regular LIST format so any number
|
|
;of hex values or strings in quotes may be entered.
|
|
|
|
ENTER:
|
|
MOV CX,5 ;5 digits in address
|
|
CALL GETHEX ;Get ENTER address
|
|
CALL GETSEG ;Convert to seg/disp format
|
|
;Adjust segment and displacement so we are in the middle
|
|
;of the segment instead of the very bottom. This allows
|
|
;backing up a long way.
|
|
SUB AH,8 ;Adjust segment 32K down
|
|
ADD DH,80H ; and displacement 32K up
|
|
PUSH AX ;Save for later
|
|
PUSH DX
|
|
CALL SCANB ;Any more parameters?
|
|
JNZ GETLIST ;If not end-of-line get list
|
|
POP DI ;Displacement of ENTER
|
|
POP ES ;Segment
|
|
GETROW:
|
|
CALL OUTDI ;Print address of entry
|
|
CALL BLANK ;Leave a space
|
|
GETBYTE:
|
|
SEG ES
|
|
MOV AL,[DI] ;Get current value
|
|
CALL HEX ;And display it
|
|
MOV AL,"."
|
|
CALL OUT ;Prompt for new value
|
|
MOV CX,2 ;Max of 2 digits in new value
|
|
MOV DX,0 ;Intial new value
|
|
GETDIG:
|
|
CALL IN ;Get digit from user
|
|
MOV AH,AL ;Save
|
|
CALL HEXCHK ;Hex digit?
|
|
XCHG AH,AL ;Need original for echo
|
|
JC NOHEX ;If not, try special command
|
|
CALL OUT ;Echo to console
|
|
MOV DH,DL ;Rotate new value
|
|
MOV DL,AH ;And include new digit
|
|
LOOP GETDIG ;At most 2 digits
|
|
;We have two digits, so all we will accept now is a command.
|
|
WAIT:
|
|
CALL IN ;Get command character
|
|
NOHEX:
|
|
CMP AL,8 ;Backspace
|
|
JZ BS
|
|
CMP AL,7FH ;RUBOUT
|
|
JZ BS
|
|
CMP AL,"-" ;Back up to previous address
|
|
JZ PREV
|
|
CMP AL,13 ;All done with command?
|
|
JZ EOL
|
|
CMP AL," " ;Go to next address
|
|
JZ NEXT
|
|
;If we got here, character was invalid. Sound bell.
|
|
MOV AL,7
|
|
CALL OUT
|
|
JCXZ WAIT ;CX=0 means no more digits
|
|
JP GETDIG ;Don't have 2 digits yet
|
|
BS:
|
|
CMP CL,2 ;CX=2 means nothing typed yet
|
|
JZ GETDIG ;Can't back up over nothing
|
|
INC CL ;Accept one more character
|
|
MOV DL,DH ;Rotate out last digit
|
|
MOV DH,CH ;Zero this digit
|
|
CALL BACKUP ;Physical backspace
|
|
JP GETDIG ;Get more digits
|
|
|
|
;If new value has been entered, convert it to binary and
|
|
;put into memory. Always bump pointer to next location
|
|
|
|
STORE:
|
|
CMP CL,2 ;CX=2 means nothing typed yet
|
|
JZ NOSTO ;So no new value to store
|
|
;Rotate DH left 4 bits to combine with DL and make a byte value
|
|
PUSH CX
|
|
MOV CL,4
|
|
SHL DH,CL
|
|
POP CX
|
|
OR DL,DH ;Hex is now converted to binary
|
|
SEG ES
|
|
MOV [DI],DL ;Store new value
|
|
NOSTO:
|
|
INC DI ;Prepare for next location
|
|
RET
|
|
EOL:
|
|
CALL STORE ;Enter the new value
|
|
JMP CRLF ;CR/LF and terminate
|
|
NEXT:
|
|
CALL STORE ;Enter new value
|
|
INC CX ;Leave a space plus two for
|
|
INC CX ; each digit not entered
|
|
CALL TAB
|
|
MOV AX,DI ;Next memory address
|
|
AND AL,7 ;Check for 8-byte boundary
|
|
JNZ GETBYTE ;Take 8 per line
|
|
NEWROW:
|
|
CALL CRLF ;Terminate line
|
|
JMP GETROW ;Print address on new line
|
|
PREV:
|
|
CALL STORE ;Enter the new value
|
|
;DI has been bumped to next byte. Drop it 2 to go to previous addr
|
|
DEC DI
|
|
DEC DI
|
|
JP NEWROW ;Terminate line after backing up
|
|
|
|
;Perform register dump if no parameters or set register if a
|
|
;register designation is a parameter.
|
|
|
|
REG:
|
|
CALL SCANP
|
|
JZ DISPREG
|
|
MOV DL,[DI]
|
|
INC DI
|
|
MOV DH,[DI]
|
|
CMP DH,13
|
|
JZ FLAG
|
|
INC DI
|
|
CALL GETEOL
|
|
CMP DH," "
|
|
JZ FLAG
|
|
MOV DI,REGTAB
|
|
XCHG AX,DX
|
|
PUSH CS
|
|
POP ES
|
|
MOV CX,REGTABLEN
|
|
REPNZ
|
|
SCAW
|
|
JNZ BADREG
|
|
OR CX,CX
|
|
JNZ NOTPC
|
|
DEC DI
|
|
DEC DI
|
|
SEG CS
|
|
MOV AX,[DI-2]
|
|
NOTPC:
|
|
CALL OUT
|
|
MOV AL,AH
|
|
CALL OUT
|
|
CALL BLANK
|
|
PUSH DS
|
|
POP ES
|
|
LEA BX,[DI+REGDIF-2]
|
|
MOV DX,[BX]
|
|
CALL OUT16
|
|
CALL CRLF
|
|
MOV AL,":"
|
|
CALL OUT
|
|
CALL INBUF
|
|
CALL SCANB
|
|
JZ RET3
|
|
MOV CX,4
|
|
CALL GETHEX1
|
|
CALL GETEOL
|
|
MOV [BX],DX
|
|
RET3: RET
|
|
BADREG:
|
|
MOV AX,5200H+"B" ;BR ERROR
|
|
JMP ERR
|
|
DISPREG:
|
|
MOV SI,REGTAB
|
|
MOV BX,AXSAVE
|
|
MOV CX,8
|
|
CALL DISPREGLINE
|
|
CALL CRLF
|
|
MOV CX,5
|
|
CALL DISPREGLINE
|
|
CALL BLANK
|
|
CALL DISPFLAGS
|
|
JMP CRLF
|
|
FLAG:
|
|
CMP DL,"F"
|
|
JNZ BADREG
|
|
CALL DISPFLAGS
|
|
MOV AL,"-"
|
|
CALL OUT
|
|
CALL INBUF
|
|
CALL SCANB
|
|
XOR BX,BX
|
|
MOV DX,[FSAVE]
|
|
GETFLG:
|
|
MOV SI,DI
|
|
LODW
|
|
CMP AL,13
|
|
JZ SAVCHG
|
|
CMP AH,13
|
|
JZ FLGERR
|
|
MOV DI,FLAGTAB
|
|
MOV CX,32
|
|
PUSH CS
|
|
POP ES
|
|
REPNE
|
|
SCAW
|
|
JNZ FLGERR
|
|
MOV CH,CL
|
|
AND CL,0FH
|
|
MOV AX,1
|
|
ROL AX,CL
|
|
TEST AX,BX
|
|
JNZ REPFLG
|
|
OR BX,AX
|
|
OR DX,AX
|
|
TEST CH,16
|
|
JNZ NEXFLG
|
|
XOR DX,AX
|
|
NEXFLG:
|
|
MOV DI,SI
|
|
PUSH DS
|
|
POP ES
|
|
CALL SCANP
|
|
JP GETFLG
|
|
DISPREGLINE:
|
|
SEG CS
|
|
LODW
|
|
CALL OUT
|
|
MOV AL,AH
|
|
CALL OUT
|
|
MOV AL,"="
|
|
CALL OUT
|
|
MOV DX,[BX]
|
|
INC BX
|
|
INC BX
|
|
CALL OUT16
|
|
CALL BLANK
|
|
CALL BLANK
|
|
LOOP DISPREGLINE
|
|
RET
|
|
REPFLG:
|
|
MOV AX,4600H+"D" ;DF ERROR
|
|
FERR:
|
|
CALL SAVCHG
|
|
ERR:
|
|
CALL OUT
|
|
MOV AL,AH
|
|
CALL OUT
|
|
MOV SI,ERRMES
|
|
JMP PRINT
|
|
SAVCHG:
|
|
MOV [FSAVE],DX
|
|
RET
|
|
FLGERR:
|
|
MOV AX,4600H+"B" ;BF ERROR
|
|
JP FERR
|
|
DISPFLAGS:
|
|
MOV SI,FLAGTAB
|
|
MOV CX,16
|
|
MOV DX,[FSAVE]
|
|
DFLAGS:
|
|
SEG CS
|
|
LODW
|
|
SHL DX
|
|
JC FLAGSET
|
|
SEG CS
|
|
MOV AX,[SI+30]
|
|
FLAGSET:
|
|
OR AX,AX
|
|
JZ NEXTFLG
|
|
CALL OUT
|
|
MOV AL,AH
|
|
CALL OUT
|
|
CALL BLANK
|
|
NEXTFLG:
|
|
LOOP DFLAGS
|
|
RET
|
|
|
|
;Trace 1 instruction or the number of instruction specified
|
|
;by the parameter using 8086 trace mode. Registers are all
|
|
;set according to values in save area
|
|
|
|
TRACE:
|
|
CALL SCANP
|
|
CALL HEXIN
|
|
MOV DX,1
|
|
JC STOCNT
|
|
MOV CX,4
|
|
CALL GETHEX
|
|
STOCNT:
|
|
MOV [TCOUNT],DX
|
|
CALL GETEOL
|
|
STEP:
|
|
MOV [BRKCNT],0
|
|
OR B,[FSAVE+1],1
|
|
EXIT:
|
|
MOV [12],BREAKFIX
|
|
MOV [14],CS
|
|
MOV [4],REENTER
|
|
MOV [6],CS
|
|
DI
|
|
MOV [64H],REENTER
|
|
MOV [66H],CS
|
|
MOV SP,STACK
|
|
POP AX
|
|
POP BX
|
|
POP CX
|
|
POP DX
|
|
POP BP
|
|
POP BP
|
|
POP SI
|
|
POP DI
|
|
POP ES
|
|
POP ES
|
|
POP SS
|
|
MOV SP,[SPSAVE]
|
|
PUSH [FSAVE]
|
|
PUSH [CSSAVE]
|
|
PUSH [IPSAVE]
|
|
MOV DS,[DSSAVE]
|
|
IRET
|
|
STEP1: JP STEP
|
|
|
|
;Re-entry point from breakpoint. Need to decrement instruction
|
|
;pointer so it points to location where breakpoint actually
|
|
;occured.
|
|
|
|
BREAKFIX:
|
|
XCHG SP,BP
|
|
DEC [BP]
|
|
XCHG SP,BP
|
|
|
|
;Re-entry point from trace mode or interrupt during
|
|
;execution. All registers are saved so they can be
|
|
;displayed or modified.
|
|
|
|
REENTER:
|
|
SEG CS
|
|
MOV [SPSAVE+SEGDIF],SP
|
|
SEG CS
|
|
MOV [SSSAVE+SEGDIF],SS
|
|
XOR SP,SP
|
|
MOV SS,SP
|
|
MOV SP,RSTACK
|
|
PUSH ES
|
|
PUSH DS
|
|
PUSH DI
|
|
PUSH SI
|
|
PUSH BP
|
|
DEC SP
|
|
DEC SP
|
|
PUSH DX
|
|
PUSH CX
|
|
PUSH BX
|
|
PUSH AX
|
|
PUSH SS
|
|
POP DS
|
|
MOV SP,[SPSAVE]
|
|
MOV SS,[SSSAVE]
|
|
POP [IPSAVE]
|
|
POP [CSSAVE]
|
|
POP AX
|
|
AND AH,0FEH
|
|
MOV [FSAVE],AX
|
|
MOV [SPSAVE],SP
|
|
PUSH DS
|
|
POP ES
|
|
PUSH DS
|
|
POP SS
|
|
MOV SP,STACK
|
|
MOV [64H],INT
|
|
MOV AL,20H
|
|
OUT BASE+2
|
|
EI
|
|
UP
|
|
CALL CRLF
|
|
CALL DISPREG
|
|
DEC [TCOUNT]
|
|
JNZ STEP1
|
|
ENDGO:
|
|
MOV SI,BPTAB
|
|
MOV CX,[BRKCNT]
|
|
JCXZ COMJMP
|
|
CLEARBP:
|
|
MOV DX,[SI+BPLEN]
|
|
LODW
|
|
PUSH AX
|
|
CALL GETSEG
|
|
MOV ES,AX
|
|
MOV DI,DX
|
|
POP AX
|
|
STOB
|
|
LOOP CLEARBP
|
|
COMJMP: JMP COMMAND
|
|
|
|
;Input from the specified port and display result
|
|
|
|
INPUT:
|
|
MOV CX,4 ;Port may have 4 digits
|
|
CALL GETHEX ;Get port number in DX
|
|
INB DX ;Variable port input
|
|
CALL HEX ;And display
|
|
JMP CRLF
|
|
|
|
;Output a value to specified port.
|
|
|
|
OUTPUT:
|
|
MOV CX,4 ;Port may have 4 digits
|
|
CALL GETHEX ;Get port number
|
|
PUSH DX ;Save while we get data
|
|
MOV CX,2 ;Byte output only
|
|
CALL GETHEX ;Get data to output
|
|
XCHG AX,DX ;Output data in AL
|
|
POP DX ;Port in DX
|
|
OUTB DX ;Variable port output
|
|
RET
|
|
|
|
;Jump to program, setting up registers according to the
|
|
;save area. Up to 10 breakpoint addresses may be specified.
|
|
|
|
GO:
|
|
MOV BX,LINEBUF
|
|
XOR SI,SI
|
|
GO1:
|
|
CALL SCANP
|
|
JZ EXEC
|
|
MOV CX,5
|
|
CALL GETHEX
|
|
MOV [BX],DX
|
|
MOV [BX-BPLEN+1],AH
|
|
INC BX
|
|
INC BX
|
|
INC SI
|
|
CMP SI,BPMAX+1
|
|
JNZ GO1
|
|
MOV AX,5000H+"B" ;BP ERROR
|
|
JMP ERR
|
|
EXEC:
|
|
MOV [BRKCNT],SI
|
|
CALL GETEOL
|
|
MOV CX,SI
|
|
JCXZ NOBP
|
|
MOV SI,BPTAB
|
|
SETBP:
|
|
MOV DX,[SI+BPLEN]
|
|
LODW
|
|
CALL GETSEG
|
|
MOV DS,AX
|
|
MOV DI,DX
|
|
MOV AL,[DI]
|
|
MOV B,[DI],0CCH
|
|
PUSH ES
|
|
POP DS
|
|
MOV [SI-2],AL
|
|
LOOP SETBP
|
|
NOBP:
|
|
MOV [TCOUNT],1
|
|
JMP EXIT
|
|
|
|
;Console input interrupt handler. Used to interrupt commands
|
|
;or programs under execution (if they have interrupts
|
|
;enabled). Control-S causes a loop which waits for any other
|
|
;character to be typed. Control-C causes abort to command
|
|
;mode. All other characters are ignored.
|
|
|
|
INT:
|
|
PUSH AX ;Don't destroy accumulator
|
|
;Output End-of-Interrupt commands to slave 8259A. This
|
|
;wouldn't be necessary if Automatic End of Interrupt mode
|
|
;worked like it was supposed to!
|
|
MOV AL,20H
|
|
OUT BASE+2
|
|
IN DATA ;Get interrupting character
|
|
AND AL,7FH ;ASCII has only 7 bits
|
|
CMP AL,"S"-"@" ;Check for Control-S
|
|
JNZ NOSTOP
|
|
CALL IN ;Wait for continue character
|
|
NOSTOP:
|
|
CMP AL,"C"-"@" ;Check for Control-C
|
|
JZ BREAK
|
|
;Just ignore interrupt - restore AX and return
|
|
POP AX
|
|
IRET
|
|
BREAK:
|
|
CALL CRLF
|
|
JMP COMMAND
|
|
REGTAB:
|
|
DB "AXBXCXDXSPBPSIDIDSESSSCSIPPC"
|
|
REGDIF: EQU AXSAVE-REGTAB
|
|
|
|
;Flags are ordered to correspond with the bits of the flag
|
|
;register, most significant bit first, zero if bit is not
|
|
;a flag. First 16 entries are for bit set, second 16 for
|
|
;bit reset.
|
|
|
|
FLAGTAB:
|
|
DW 0
|
|
DW 0
|
|
DW 0
|
|
DW 0
|
|
DB "OV"
|
|
DB "DN"
|
|
DB "EI"
|
|
DW 0
|
|
DB "NG"
|
|
DB "ZR"
|
|
DW 0
|
|
DB "AC"
|
|
DW 0
|
|
DB "PE"
|
|
DW 0
|
|
DB "CY"
|
|
DW 0
|
|
DW 0
|
|
DW 0
|
|
DW 0
|
|
DB "NV"
|
|
DB "UP"
|
|
DB "DI"
|
|
DW 0
|
|
DB "PL"
|
|
DB "NZ"
|
|
DW 0
|
|
DB "NA"
|
|
DW 0
|
|
DB "PO"
|
|
DW 0
|
|
DB "NC"
|
|
|
|
;Initialization table. First byte of each entry is no.
|
|
;of bytes to output to the corresponding port. That
|
|
;many initialization bytes follow.
|
|
|
|
INITTABLE:
|
|
;Port BASE+0 - Master 8259A. Intialization Command Word (ICW)
|
|
;One sets level-triggered mode, multiple 8259As, require
|
|
;ICW4.
|
|
DB 1
|
|
DB 19H
|
|
;Port BASE+1 - Master 8259A. ICW2 sets vector base to 10H
|
|
;ICW3 sets a slave on interrupt input 1; ICW4 sets buffered
|
|
;mode, as a master, with Automatic End of Interrupt, 8086
|
|
;vector; Operation Command Word (OCW) One sets interrupt
|
|
;mask to enable line 1 (slave 8259A) only.
|
|
DB 4
|
|
DB 10H,2,0FH,0FDH
|
|
;Port BASE+2 - Slave 8259A. ICW1 sets level-triggered mode,
|
|
;multiple 8259As, require ICW4.
|
|
DB 1
|
|
DB 19H
|
|
;Port BASE+3 - Slave 8259A. ICW2 sets vector base to 18H
|
|
;ICW3 sets slave address as 1; ICW4 sets buffered mode,
|
|
;as slave, with Automatic End of Interrupt (which doesn't
|
|
;work in slaves), 8086 vector; OCW1 sets interrupt mask
|
|
;to enable line 1 (serial receive) only.
|
|
DB 4
|
|
DB 18H,1,0BH,0FDH
|
|
;Port Base+4 - 9513 Data. 9513 has previously been set
|
|
;up for Counter 5 mode register with auto increment. Thus
|
|
;mode is set to 0B63H, which is no gating, count source is
|
|
;F1 (4 MHz), reload from load or hold, count down repetitively
|
|
;in binary, with output toggle. Load register is set to
|
|
;0007H, and Hold register is set to 0006H. Thus we
|
|
;alternately divide by 7 and 6, which is divided by 2 by
|
|
;the output toggle, thus providing a square wave of
|
|
;4 MHz/13 = 307.7 kHz, which divided by 16 in the 8251A
|
|
;provides 19,230 baud (0.16% high).
|
|
DB 6
|
|
DB 63H,0BH,7,0,6,0
|
|
;Port BASE+5 - 9513 Control. Load and arm counter 5,
|
|
;enabling baud rate generation. Then select counter
|
|
;5 mode register, in case baud rate wasn't right.
|
|
DB 2
|
|
DB 70H,5
|
|
;Port BASE+6 - 8251A Data. No initialization to this port.
|
|
DB 0
|
|
;Port BASE+7 - 8251A Control. Since it is not possible to
|
|
;know whether the 8251A next expects a Mode Instruction or
|
|
;a Command Instruction, a dummy byte is sent which could
|
|
;safely be interpreted as either but guarantees it is now
|
|
;expecting a Command. The command sent is Internal Reset
|
|
;which causes it to start expecting a mode. The mode sent
|
|
;is for 2 stop bits, no parity, 8 data bits, 16X clock.
|
|
;This is followed by the command to error reset, enable
|
|
;transmitter and receiver, set RTS and DTR to +12V.
|
|
DB 4
|
|
DB 0B7H,77H,0CEH,37H
|
|
HEADER: DM 13,10,10,"SCP 8086 Monitor 1.5",13,10
|
|
SYNERR: DB '^'
|
|
ERRMES: DM " Error",13,10
|
|
BACMES: DM 8,32,8
|
|
|
|
;Disk boot. Select one of the following routines by
|
|
;setting the equates at the start of this program.
|
|
|
|
BOOT:
|
|
PUSH DI
|
|
|
|
;************************************************
|
|
|
|
;Boot for Cromemco 4FDC disk controller with either
|
|
;large or small disks. Loads track 0, sector 1 into LOAD.
|
|
|
|
IF CROMEMCO4FDC
|
|
|
|
DISK: EQU 30H
|
|
|
|
MOV AL,1
|
|
OUT 2 ;Reset 4FDC serial I/O
|
|
MOV AL,84H
|
|
OUT 0 ;and set for 300 baud
|
|
MOV AL,7FH
|
|
OUT 4
|
|
MOV DL,21H
|
|
RETRY:
|
|
MOV AL,0D0H
|
|
OUTB DISK
|
|
READY:
|
|
INB DISK
|
|
ROR AL
|
|
JC READY
|
|
XOR DL,10H
|
|
MOV AL,DL
|
|
OUTB DISK+4
|
|
MOV DI,LOAD
|
|
MOV AL,12
|
|
OUTB DISK
|
|
HOME:
|
|
INB DISK+4
|
|
ROR AL
|
|
JNC HOME
|
|
INB DISK
|
|
AND AL,98H
|
|
JNZ RETRY
|
|
MOV AL,1
|
|
OUTB DISK+2
|
|
MOV CX,80H
|
|
MOV AL,DL
|
|
OR AL,80H
|
|
OUTB DISK+4
|
|
MOV AL,8CH
|
|
OUTB DISK
|
|
READ:
|
|
INB DISK+4
|
|
ROR AL
|
|
JC DONE
|
|
INB DISK+3
|
|
STOB
|
|
LOOP READ
|
|
WSTAT:
|
|
INB DISK+4
|
|
ROR AL
|
|
JNC WSTAT
|
|
DONE:
|
|
INB DISK
|
|
AND AL,9CH
|
|
JNZ RETRY
|
|
ENDIF
|
|
|
|
;***************************************************
|
|
|
|
;Boot for North Star disk, single density.
|
|
;Loads track 0, sector 0 into address LOAD
|
|
;Bug in North Star boot fixed 5-26-81.
|
|
|
|
IF NORTHSTARSD
|
|
|
|
;Disk command equates
|
|
|
|
SEL: EQU 1
|
|
STP1: EQU 9
|
|
STP2: EQU 8
|
|
NOP: EQU 10H
|
|
SEC: EQU 14H
|
|
STPOUT: EQU 1CH
|
|
RD: EQU 40H
|
|
BST: EQU 20H
|
|
|
|
PUSH DS
|
|
MOV AX,0FEB8H
|
|
MOV DS,AX
|
|
MOV AL,[SEL]
|
|
MOV CX,20
|
|
MOTOR:
|
|
CALL SECTOR
|
|
LOOP MOTOR
|
|
CHKTRK:
|
|
TEST B,[STPOUT],1
|
|
JNZ ONTRACK
|
|
MOV AL,[STP1]
|
|
AAM
|
|
MOV AL,[STP2]
|
|
CALL SECTOR
|
|
CALL SECTOR
|
|
JP CHKTRK
|
|
SECTOR:
|
|
MOV AL,[SEC] ; Reset sector flag.
|
|
SECLP:
|
|
TEST B,[NOP],80H ; Wait for sector flag.
|
|
JZ SECLP
|
|
RET
|
|
ONTRACK:
|
|
MOV DI,LOAD
|
|
MOV CX,280
|
|
MOV BX,RD+NOP
|
|
GETSEC:
|
|
CALL SECTOR
|
|
TEST B,[BST+NOP],0FH ; Test for sector zero.
|
|
JNZ GETSEC
|
|
GETSYNC:
|
|
TEST B,[NOP],4
|
|
LOOPZ GETSYNC
|
|
JZ ONTRACK
|
|
MOV CX,100H
|
|
XOR DL,DL
|
|
AAD
|
|
READ:
|
|
MOV AL,[BX]
|
|
STOB ;Uses ES
|
|
XOR DL,AL
|
|
ROL DL
|
|
AAD
|
|
LOOP READ
|
|
MOV AL,[BX]
|
|
CMP AL,DL
|
|
JNZ ONTRACK
|
|
POP DS
|
|
ENDIF
|
|
|
|
;************************************************
|
|
|
|
;Boot for Tarbell disk controllers. Load track 0,
|
|
;sector 1 into LOAD.
|
|
|
|
IF TARBELL
|
|
|
|
DISK: EQU 78H
|
|
|
|
JP RETRY
|
|
DCOM:
|
|
OUTB DISK
|
|
MOV AL,50
|
|
HOLD:
|
|
DEC AL
|
|
JNZ HOLD
|
|
RET
|
|
|
|
RETRY:
|
|
MOV AL,0D0H
|
|
CALL DCOM
|
|
READY:
|
|
INB DISK
|
|
ROR AL
|
|
JC READY
|
|
MOV DI,LOAD
|
|
MOV AL,0EH ;Home command @ 10ms/track
|
|
CALL DCOM
|
|
INB DISK+4
|
|
INB DISK
|
|
AND AL,98H
|
|
JNZ RETRY
|
|
MOV AL,1
|
|
OUTB DISK+2
|
|
MOV CX,80H
|
|
MOV AL,8CH
|
|
CALL DCOM
|
|
READ:
|
|
INB DISK+4
|
|
ROL AL
|
|
JNC DONE
|
|
INB DISK+3
|
|
STOB
|
|
LOOP READ
|
|
WSTAT:
|
|
INB DISK+4
|
|
ROL AL
|
|
JC WSTAT
|
|
DONE:
|
|
INB DISK
|
|
AND AL,9CH
|
|
JNZ RETRY
|
|
ENDIF
|
|
|
|
;***************************************************
|
|
|
|
IF OTHER
|
|
|
|
;User may insert customized disk boot here. All
|
|
;registers are available, stack pointer is valid
|
|
;and interrupts are enabled. Stack should be at
|
|
;same level on fall-through to code below. Last
|
|
;address available is 07DF hex.
|
|
|
|
ORG 7E0H ;Simulate boot of maximum length
|
|
|
|
ENDIF
|
|
|
|
;***************************************************
|
|
|
|
;Successful read
|
|
MOV [CSSAVE],0
|
|
MOV [IPSAVE],LOAD
|
|
POP DI
|
|
JMP GO
|
|
|