Category : Assembly Language Source Code
Archive   : D86BIOS4.ZIP
Filename : BIOS.8

 
Output of file : BIOS.8 contained in archive : D86BIOS4.ZIP
;---------------
; BIOS module for the D86 debugger
;---------------

; Copyright 1987,88 Eric Isaacson. All rights reserved. Permission to
; copy and use this module is granted ONLY for machines registered for both
; the A86 assembler and the D86 debugger.

; Current support: IBM-PC, Wang PC, TI-PC, Sanyo 555, Tandy 2000,
; and Sirius/Victor 9000, DEC RAINBOW, and Zenith Z-100.

; This module defines the BIOS interface for my D86 debugger. I am publishing
; it to assist those who wish to assist me in implementing D86 on machines not
; BIOS-compatible with the IBM-PC. To support a non-standard BIOS, we must
; provide new keyboard codes, new action routines, and several other data
; quantities. This module provides a routine BIOS_INIT that sets everything
; up according to the machine being used. BIOS_INIT must do four things:
;
; 1. The routine must figure out what machine we are running on. The best
; way to do this is to find a machine-specific identifier in a fixed
; area of memory (ROM or the BIOS). If you can locate such an identifier,
; you can simply create an entry in the BIOS_SIGS structure below.
;
; 2. The routine must point SI to a special data structure (which I'll
; describe shortly), then call the routine NEW_KEYS, to propagate the
; items in the structure to the necessary places throughout the debugger.
;
; 3. The routine must perform any initializations necessary for this BIOS.
; For example, WANG_CONFIG locates and stores the port number for hardware
; that enables video access on the Wang.
;
; 4. The routine must decide if the debugger is running on the same screen as
; the user program. If it is, it must move the user's cursor to the lower
; left corner of the screen.
;
; The structure fed to NEW_KEYS contains all the data necessary for ongoing
; debugger execution under the new BIOS. See the structure WANG_KEYS for a
; prototype. The structure consists of the following:
;
; * a byte giving the keyboard code for the debugger's HELP key.
;
; * a byte declaring the difference between your BIOS's key codes for
; function keys, and the IBM BIOS's key codes. Your BIOS_KEY must return
; consecutive values for the function keys F1 through F10. You should
; declare this byte to be X - FUNC, where X is one less than the code
; returned by the F1 key. For example, since the Wang F1 key returns hex
; 080, the byte is declared 07F - FUNC.

; * a number of bytes giving code values for all the other control keys
; used by the debugger. This list will be expanding with new versions;
; see WANG_KEYS for the current version's list. You should precede the list
; with the L1 label, and follow it with the declaration N_CONTROL_KEYS EQU
; $-L1, exactly as shown. Don't change the name N_CONTROL_KEYS; the
; redeclaration of the same name insures that you've gotten the right number
; of codes into the table.
;
; * a word pointing to a message string with the name of the help-key. If your
; keyboard has a key labelled HELP, use the name HELP_HELP as in WANG_KEYS.
; If there is another key nonexistent on the IBM-PC (e.g. F11), then put
; a new name (e.g. F11_HELP) following DW; I'll supply the definition for
; the new name. If there is no extra key and no HELP key, use Alt-F10 as
; on the IBM-PC, and declare DW ALTF10_HELP here.
;
; * the label L2: to be used to verify the number of following bytes.
;
; * pointers to your BIOS's versions of the procedures VID_COPY, VID_ATTR,
; VID_FIX, BIOS_BELL, and BIOS_KEY, described shortly. Substitute the name
; of your machine for VID and BIOS in the generic names; e.g. the WANG_KEYS
; structure has WANG_COPY, WANG_ATTR, WANG_BELL, and WANG_KEY.
;
; * a word giving the segment register value for video memory on your machine.
; The debugger will supply this value in the ES register when it calls
; VID_COPY and VID_ATTR. That is all the debugger does with it; so this
; value can really be anything that the BIOS's versions of VID_COPY and
; VID_ATTR want.
;
; * a byte giving the attribute value for normal video. This attribute will
; be in effect for the entire debugger screen, except for the location of
; the debugger's cursor.
;
; * a byte giving the attribute value for reverse video. This attribute will
; be used to mark the debugger cursor.
;
; * the declaration N_BIOS_CALLS EQU ($-L2)/2 Again, don't change the name;
; the redeclaration of the name insures you didn't leave anything out.
;
; This completes the description of the structure fed to NEW_KEYS. After
; BIOS_INIT is called, the debugger will keep calling 7 action routines to
; perform its interactive I/O. The routines must perform actions as
; follows:

; VID_COPY copies CL bytes of characters from DS:SI to the video memory whose
; location is given by ES:DI. The characters should have the attribute
; NORM_ATTR, which the caller will place into AH for VID_COPY's convenience.
; VID_COPY must place the character byte and the attribute byte into each
; output video word. VID_COPY must return with BL preserved, the high byte
; of SI preserved (it will be if you leave SI pointing beyond the bytes
; copied), and DI advanced beyond the video memory just output. The caller
; assumes that video memory can be found at the value of VIDEO_SEG set by
; BIOS_INIT, starting at offset 0, and proceeding consecutively, one 16-bit
; memory word for every character. The caller will set CH=0 for its first
; call, so that VID_COPY can use CX as the count; but if it does, it must
; return CH=0 for subsequent calls.

; VID_ATTR places the attribute byte AL into the video word whose location is
; given by ES:DI. The character byte of the video word must not be disturbed.

; VID_FIX restores a video screen that might have been clobbered by programs
; external to the debugger. If VID_COPY copies all characters to video memory
; every time, then VID_FIX should RET without doing anything. If, however,
; VID_COPY tries to keep track of which characters are already on the screen,
; and suppress video output for those that are, then VID_FIX should disable
; the suppression feature, call the debugger's routine REFRESH to update the
; whole screen, then re-enable the suppression feature.

; BIOS_BELL rings the bell. NOTE that it is not acceptible for BIOS_BELL to
; use the MS-DOS write routine to send a bell control-code to standard output;
; if it did, then the debugger couldn't debug programs that have redirected
; their standard output-- the bell code would go to the user program's output
; file, and not be translated into a beep.

; BIOS_KEY returns in AL a code for a single keystroke. The code should be
; compatible with the values placed into the debugger's function tables by
; BIOS_INIT. If there is no keystroke available, BIOS_KEY should wait until
; there is one. BIOS_KEY should return on each individual key, and not wait
; for any line-editing to take place.

; BIOS_SAVE saves whatever there is about the user program's BIOS state that
; might be clobbered by the debugger. Currently, the only such thing is
; the user's cursor position on the Sanyo. So on all machines but the Sanyo,
; this is a "do-nothing" routine.

; BIOS_RESTORE restores the BIOS state saved by BIOS_SAVE. Again, this routine
; does nothing except on the Sanyo, on which it restores the user cursor
; position, clobbered because D86 must use BIOS calls to write to the Sanyo
; screen.


BIOS_SIGNATURE MACRO
DB #1 ; case number for the machine being sought
DB #NL-2 ; number of far pointers to look at
DB >M2 ; length byte for the following string
M1:
DB #2 ; BIOS signature string we are looking for
M2 EQU $-M1 ; we calculate the forward-reference length byte here
#RX3L ; loop for remaining macro operands
DD #X ; far pointers to each place where the string might be found
#ER
#EM

BIOS_SIGS:
BIOS_SIGNATURE 6,'Texas I',0F400:0A022
BIOS_SIGNATURE 7,'Tandy' ,0FC00:002F ,0FC00:0032
BIOS_SIGNATURE 10,'Rainbow',0FE00:0166 ,0FC00:03F2, 0FE00:0163
BIOS_SIGNATURE 12, 0E9 ,040:0
DB 0FF ; terminator byte

L0: ; jump table for each type of machine
DW IBM_MONO_CONFIG ; 1
DW IBM_CGA_CONFIG ; 2
DW IBM_EGA_CONFIG ; 3
DW IBM_CONFIG ; 4
DW WANG_CONFIG ; 5
DW TIPC_CONFIG ; 6
DW TANDY_CONFIG ; 7
DW SANYO_CONFIG ; 8
DW SIRIUS_CONFIG ; 9
DW DEC_CONFIG ; 10
dw ibm_mono_config
; DW HP_CONFIG ; 11
DW Z100_CONFIG ; 12
L6 EQU ($-L0)/2

L2: ; machine type AL is not 1
CMP AL,0FF ; is it 0FF?
JNZ >L7 ; jump if not -- we are on an IBM-PC
MOV ES,AX,0FFFF ; it was-- address the end of ROM
ES CMP AX,[6] ; are there FFFF's beyond the boot-JMP?
MOV AL,8 ; load Sanyo index, in case there were
JE >L1 ; jump if there were-- it is a Sanyo
L7: ; all non-IBM tests fail: let's assume IBM compatibility
MOV ES,CS ; point ES to CS, where IBM_CONFIG wants it
MOV DH,0 ; the case number is zero-- no EGA vs. CGA decided yet
JMP IBM_CONFIG ; jump to IBM-compatible configuration code

BIOS_INIT:
MOV DS,CS ; insure that DS points to our code segment
MOV AL,SWITCH'B' ; fetch the BIOS switch setting if there was one
DEC AX ; eliminate 0 setting from consideration
CMP AL,L6 ; was there an explicit BIOS switch setting?
JB >L5 ; jump if there was, to the matching case
MOV SI,BIOS_SIGS ; point to the BIOS-signatures data structure
LODSW ; fetch the first case number and far-pointer-count
L3: ; loop here for each subsequent machine's record
XCHG DX,AX ; swap the case number to DL, pointers count to DH
LODSB ; fetch the length byte
CBW ; extend the length AL to AX
XCHG CX,AX ; swap the length into CX
MOV BX,SI ; save the string pointer in BX
ADD SI,CX ; advance SI beyond the string, to the far pointers
L8: ; loop here for each far pointer
LODSW ; fetch the offset of the pointer
XCHG DI,AX ; swap the offset into DI
LODSW ; fetch the segment of the pointer
MOV ES,AX ; move the segment into ES
XCHG BX,SI ; swap the string pointer into SI
PUSH CX,SI ; save the count
REPE CMPSB ; see if the string is at this far pointer
POP SI,CX ; restore the count
XCHG SI,BX ; swap string pointer to BX, template pointer to SI
MOV AL,DL ; fetch the case number in case the string matched
JE >L1 ; jump if the string matched to act upon the case
DEC DH ; count down far pointers
JNZ L8 ; loop if there is another far pointer
LODSW ; fetch the next record's case number and pointers count
CMP AL,0FF ; did we load the terminator byte instead?
JNE L3 ; loop if not, to process the next machine record
MOV AL,0 ; no string match: load the machine type, already plugged in
MACHINE_TYPE EQU B[$-1]
CMP AL,1 ; is it a Wang?
JNE L2 ; jump if not
MOV AL,5 ; load index for Wang
L1: ; we will jump to case number AL
DEC AX ; decrement so the first case is 0 not 1
L5:
CBW ; extend the case number from AL into AX
MOV DH,AL ; save the case number in DH
ADD AX,AX ; double the case number, to address a word pointer
XCHG BX,AX ; swap the index into BX, for addressing
MOV ES,CS ; restore ES=CS, for the benefit of the case code
JMP L0[BX] ; jump to the appropriate case for this machine


; IBM_CONFIG is the BIOS_INIT routine for the IBM-PC. Since its NEW_KEY
; values are the defaults, we do not need to call NEW_KEY.

IBM_MONO_CONFIG:
IBM_CGA_CONFIG:
IBM_EGA_CONFIG:
IBM_CONFIG:
MOV AH,15 ; function number for GET_VIDEO_MODE
INT 16 ; call the BIOS to get the mode
CMP AL,7 ; are we in monochrome mode?
MOV AX,0B000 ; load monochrome map location in case yes
IF B MOV AH,0B8 ; if not then load color map location
MOV BL,0 ; initial BL set for non-screen-swapping
TEST B SWITCH'V',1 ; is the V flag set?
IF NZ MOV BL,8 ; if it is then we will swap screens
XOR AH,BL ; switch interfaces if we saw a +V in invocation
MOV VIDEO_SEG,AX ; store the location of physical video
TEST AH,8 ; are we on a CGA or EGA video board?
JZ >L1 ; skip if not
CMP DH,1 ; have we already selected which, via the B case?
JAE >L5 ; skip if we have
PUSH BX ; we haven't selected CGA vs. EGA: save BX
MOV AH,012 ; BIOS function code for GET_EGA_STATUS
MOV BL,010 ; load an impossible status

INT 16 ; set BL to the EGA status
CMP BL,010 ; was there an EGA status?
POP BX ; restore clobbered register
L5: ; NE is set if we have an EGA
MOV AX,EGA_ATTRS ; load EGA attributes in case we do have an EGA
JNE >L2 ; jump if we do have an EGA
MOV AX,CGA_ATTRS ; not an EGA: load CGA attributes
TEST B SWITCH'F',1 ; is the FAST switch set?
JNZ >L2 ; skip if it is
MOV VID_COPY,COLOR_COPY ; CGA and no FAST switch: change copy routine
MOV BIOS_RESTORE,COLOR_RESTORE ; activate the screen-restore function
L2:
MOV ATTR_BYTES,AX ; set the attribute bytes to AL and AH
L1:
TEST BL ; are we the same screen as the user program?
JNZ RET ; return if we are not-- no need to move cursor
SET_IBM_LOW_LEFT:
MOV BH,0 ; page number is zero
MOV DX,24 BY 0 ; we will move the cursor to row 24, column 0
SET_IBM_CURSOR:
MOV AH,2 ; video BIOS function number for SET CURSOR POSITION
INT 16 ; call the BIOS to put user cursor in lower left corner
RET


; IBM_FIX performs a fixup of a trashed screen on an IBM machine.

; COLOR_RESTORE checks to see if the debugger screen has been trashed. If it
; has, we restore the screen.

COLOR_RESTORE:
PUSH DS ; save register across call
MOV DS,AX,0B800 ; point DS to the video screen
MOV DX,03DA ; load the port number for reading the video status
L2: ; loop here to wait for vertical retrace
IN AL,DX ; input the status
TEST AL,1 ; mask the retrace bit
JZ L2 ; loop if we are not in vertical retrace
CMP B[2400],'A' ; check the "A" of the fixed "AX" display
POP DS ; restore clobbered register
JE RET ; return if the "A" has not rolled away or been trashed
IBM_FIX:
CS PUSH VID_COPY ; save the old VID_COPY value
CS MOV VID_COPY,MONO_COPY ; coerce it to MONO_COPY, to blindly copy all
CALL REFRESH ; refresh the screen; let the snow scatter!
CS POP VID_COPY ; restore the old VID_COPY value
IBM_SAVE:
IBM_RESTORE:
RET


; MONO_COPY is the VID_COPY routine for an IBM monochrome video board. The
; characters occupy the lower byte of the DI-pointed words. We can afford
; to rewrite the entire screen on each refresh; so no special action needs to
; be taken. We do complicated looping to make the routine as fast as
; possible.

MONO_COPY:
SHR CX,1 ; is the character count odd?
JC >L5 ; jump if yes, to special code
L1:
SHR CX,1 ; is the character count a multiple of 4?
JC >L6 ; jump if not, to special code
L2: ; loop here to copy every 4 bytes
LODSB ; load the character from the source
STOSW ; output the character, with the standard attribute byte
LODSB ; char # 2
STOSW
LODSB ; char # 3
STOSW
LODSB ; char # 4
STOSW
LOOP L2 ; loop for the next 4 characters
RET

L5: ; the character count was odd
MOVSB ; copy the odd character to the video buffer
INC DI ; advance beyond the attribute byte
JCXZ RET ; return if count is depleted
JMP L1 ; join even code

L6: ; the character count is 2 mod 4
LODSB ; load one character
STOSW ; output it and the attribute-- count now 1 mod 4
LODSB ; load second character
STOSW ; output it-- count now 0 mod 4
JCXZ RET ; return if count is depleted
JMP L2 ; join multiple-of-4 code


; COLOR_COPY is the VID_COPY routine for an IBM Color Graphics Adapter board.
; The characters occupy the lower byte of the DI-pointed words. We must wait
; for vertical retrace to output our data, to avoid annying "snow" on the
; screen. So we can't afford to output the entire buffer every time. So we
; maintain at [SI+81] a copy of what's already on the screen for [SI], and we
; output only if the buffer is new.

COLOR_COPY:
PUSH BX ; preserve BX across the call
MOV DX,03DA ; load the port number for reading the video status
SKIP2 ; skip to the LODSB instruction
L0: ; loop here for every character that is already out there
INC DI ; advance the output pointer beyond the character
L1: ; loop here after a non-matching character was stored
INC DI ; advance beyond the following attribute byte
LODSB ; fetch the next character
MOV BL,AL ; save the character in BL
XCHG AL,[SI+79] ; swap it with the already-out-there value
CMP AL,BL ; is the character already out there?
LOOPE L0 ; loop if it is
JE >L4 ; jump if the characters are exhausted
L2: ; loop here to wait for vertical retrace
IN AL,DX ; input the status
TEST AL,1 ; mask the retrace bit
JZ L2 ; loop if we are not in vertical retrace
MOV AL,BL ; re-fetch the character to be output
STOSB ; output the character
INC CX ; undo the previous LOOPE's decrement of CX
LOOP L1 ; loop to check for another output character
INC DI ; advance beyond the attribute byte of the last character
POP BX ; restore clobbered register
RET

L4: ; matching character was the last in the buffer
INC DI,2 ; advance beyond the output video word
POP BX ; restore clobbered register
RET


; IBM_ATTR is the VID_ATTR routine for IBM-PC compatible computers. The
; attribute byte is the high byte of the DI-pointed video word.

IBM_ATTR:
INC DI ; advance to the high, attribute byte
STOSB ; output the attribute code AL to the byte
RET


; IBM_KEY is the BIOS_KEY routine for IBM_PC compatible computers. We must
; transform the two-byte code returned by the IBM BIOS into the single
; code AL expected by the rest of the debugger.

IBM_KEY:
MOV AH,0 ; function code for GET KEY
INT 016 ; get the keystroke from the IBM BIOS
TEST AL ; is the return AL nonzero?
JNZ RET ; if yes then AL is our return code
MOV AL,AH ; AL is zero, so AH determines the return code
ADD AL,080-16 ; shift the values into a range not seen directly in AL
RET


; IBM_BELL is the BIOS_BELL routine for IBM-PC compatible computers. We
; output the code 07 to the BIOS's console output routine.

IBM_BELL:
MOV AX,0E07 ; AH= console out function number; AL="BELL" control code
INT 010 ; output BELL to the console
RET


; NEW_KEYS reassigns the keyboard codes and the action routines for a non-
; IBM-compatible BIOS. We are called with CS:SI pointing to a table of
; various new values, whose format is identical to the one given by
; WANG_KEYS below. The new values are plugged into the various tables
; in the debugger, so that correct actions are taken for the non-compatible
; machine.

NEW_KEYS:
LODSB ; load the first byte of the table
MOV HELP_KEY,AL ; first byte is the code for HELP_KEY
LODSB ; load the second byte
ADD SWITCH_KEY,AL ; byte 2 is (new-IBM) function-key-codes-difference
MOV DI,CTRL_JUMPS+2 ; point to the control-jumps table
MOV CX,N_FUNCS ; load the number of function-keys in that table
L1: ; loop here to adjust each function-key code
ADD [DI],AL ; add the code into the table entry
ADD DI,3 ; advance to the next table entry
LOOP L1 ; loop to adjust the next table entry
MOV CL,N_CONTROL_KEYS ; load the number of subsequent keys in the table
L2: ; loop here to plug in the new value for each key
MOVSB ; copy the new key code to the function table
INC DI,2 ; advance output pointer to the next key code
LOOP L2 ; loop to plug in the next key code
LODSW ; fetch the message-pointer to the name of HELP key
MOV HELP_MSG,AX ; plug the pointer into the messages-string
MOV DI,BIOS_CALLS ; point to the table of action routines
MOV CX,N_BIOS_CALLS ; load the count of words in action-routine-table
REP MOVSW ; copy the new pointers to the table
RET


; GET_MACHINE_TYPE sets the variables MACHINE_TYPE and SUBDIR_CHAR.

GET_MACHINE_TYPE:
MOV AH,030 ; MS-DOS function number for GET_DOS_VERSION
INT 33 ; we call this to get the machine number in BH
MOV MACHINE_TYPE,BH
DEC BH ; are we on a Wang PC?
IF Z MOV SUBDIR_CHAR,'/'; if yes then switch the subdirectory character
RET





  3 Responses to “Category : Assembly Language Source Code
Archive   : D86BIOS4.ZIP
Filename : BIOS.8

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/