From: Ya`akov Miles To: Info-IBMPC at MIT-MC Re: BIOS Musings You may be interested in a history of where this BIOS came from, and how it arrived in its present form. A heavily patched, partially-functionally BIOS (with no copyright statement, or other visible indication of origin) was supplied with my IBM-PC/xt compatible 10 mHz motherboard. In order to get my motherboard to function correctly, in other words, to work with the parity interrupt enabled and to operate with the NEC "V20", it was necessary to disassemble and thoroughly go thru this "anonymous" bios, which was hinted as supplied by Taiwan, while limping along on a name brand bios, as supplied on my previous motherboard by a different vendor. In the course of this disassembly, aided by comparison with the published IBM-PC/xt listings, it became apparent that the synchronization on horizontal retrace in the video INT 10h service was the root cause of the failure to operate with the NEC "V20", and that correcting it to correspond with logic (as in IBM's bios) caused the glitch to disappear. I am unable to account as to why several name brand BIOS brands (excluding IBM's) had similar glitches - maybe they they were produced from similar source code, although this seems unlikely. In any case, as evidenced by DEBUG, some of these name-brand BIOS were full of machine-level patches - did the vendor ever bother to reassemble and optimize the source code. The code that I examined was full of recursive INT(errupt) instructions, which did not to contribute to screaming fast BIOS. Therefore, the assembly code was rearranged so as to eliminate some of the unnecessary CALL, JMP, and especially INT instructions, as the optimization proceeded with the later releases. The bios is (c)Anonymous, because there was no indication of the original authors... ps: While playing around with my 10 mHz motherboard, I encountered an unusual program called HELPME.COM, which ran at a higher pitch than normal. Since this program behaved normally on other (8 mHz) turbo motherboards, my curiousity was aroused. This eventually led me to discover that the 10 mHz motherboard was refreshed in hardware by channel 1 of the 8253 timer ic, and that this channel appeared to be counting down from an unusually fast oscillator. Maybe this could cause problems with other programs... Date: Wed, 13 Jan 88 04:26 PST From: Subject: BIOS.ASM update INFO-IBMPC librarys To: hicks@walker-emh.arpa Page 80,132 Title BIOS - For Intel 8088 or NEC "V20" turbo motherboards. Use MASM 4.0 ; ; This bios will work on IBM-PC/xt and many other compatibles that share a ; similar design concept. You do not need to have a turbo motherboard to ; use this bios, but if you do, then use the following key sequence ; CTRL ALT - ; to toggle the computer speed between fast and slow (=IBM compatible) ; ; This BIOS can produce the following error messages at IPL time ; ER_BIOS equ 01h ; Bad ROM bios checksum, patch last byte ER_RAM equ 02h ; Bad RAM in main memory, replace ER_CRT equ 04h ; Bad RAM in video card, replace ER_MEM equ 10h ; Bad RAM in vector area, replace ER_ROM equ 20h ; Bad ROM in expansion area, bad checksum ; ; The last two bytes have to be patched with DEBUG as follows ; ; FFFF 00.xx ( avoid ER_BIOS on bootstrap ) -------------------- ; FFFE 00.FE ( leaves IBM-PC/xt signature ) ----------------- | ; | | ; where "xx" results in a zero checksum for the whole BIOS rom, for ex | | ; | | ; masm BIOS; ( Assemble BIOS source code) | | ; link BIOS; ( Link the BIOS object code) | | ; debug BIOS.EXE ( Exe2bin BIOS binary code) | | ; -nBIOS.BIN ( Name of the output binary) | | ; -eCS:FFFE ( Opens BIOS signature byte) | | ; .FE ( Leave IBM-PC/xt signature) <-- | ; -eCS:FFFF ( Opens BIOS checksum byte) | ;; -------> .DC ( Force ROM checksum = zero) <----- ;; -rBX ( Opens hi order byte count) ;; :0 ( ... must be 0 bytes long) ;; -rCX ( Opens lo order byte count) ;; :2000 ( ... BIOS 2000 bytes long) ;; -wCS:E000 ( Output to BIOS.BIN file) ;; -q ;; ;; You must correct the checksum by manually patching the last byte so as the ;; the entire 2764-2 eprom sums to zero. I wish DEBUG could checksum blocks. ; ; ************************Miscellaneous definitions************************ ; * MAX_MEMORY =704 ; Maximum kilobytes of memory allowed * ;SLOW_FLOPPY =1 ; Define to run floppy always at 4.77 mHz * ; * ; ************************Miscellaneous definitions************************ ; entry macro x pad =BANNER - $ + x - 0E000h if pad LT 0 .err %out 'No room for ENTRY point' endif if pad GT 0 db pad DUP(0FFh) endif endm ; jmpf macro x,y db 0EAh; dw y,x endm ; retf macro x ifb db 0CBh else db 0CAh dw x endif endm ; LF equ 0Ah CR equ 0Dh ; .SALL ; Suppress Macro Expansions .LFCOND ; List False Conditionals ; ASSUME DS:code, SS:code, CS:code, ES:code data SEGMENT at 40h ; IBM compatible data structure dw 4 dup(?) ; 40:00 ; RS232 com. ports - up to four dw 4 dup(?) ; 40:08 ; Printer ports - up to four dw ? ; 40:10 ; Equipment present word ; + (1 iff floppies) * 1. ; + (# 64K sys ram ) * 4. ; + (init crt mode ) * 16. ; + (# of floppies ) * 64. ; + (# serial ports) * 512. ; + (1 iff toy port) * 4096. ; + (# parallel LPT) * 16384. db ? ; 40:12 ; MFG test flags, unused by us dw ? ; 40:13 ; Memory size, kilobytes db ? ; 40:15 ; IPL errors<-table/scratchpad db ? ; ...unused ;---------------[Keyboard data area]------------; db ?,? ; 40:17 ; Shift/Alt/etc. keyboard flags db ? ; 40:19 ; Alt-KEYPAD char. goes here dw ? ; 40:1A ; --> keyboard buffer head dw ? ; 40:1C ; --> keyboard buffer tail dw 16 dup(?) ; 40:1E ; Keyboard Buffer (Scan,Value) ;---------------[Diskette data area]------------; db ? ; 40:3E ; Drive Calibration bits 0 - 3 db ? ; 40:3F ; Drive Motor(s) on 0-3,7=write db ? ; 40:40 ; Ticks (18/sec) til motor off db ? ; 40:41 ; Floppy return code stat byte ; 1 = bad ic 765 command req. ; 2 = address mark not found ; 3 = write to protected disk ; 4 = sector not found ; 8 = data late (DMA overrun) ; 9 = DMA failed 64K page end ; 16 = bad CRC on floppy read ; 32 = bad NEC 765 controller ; 64 = seek operation failed ;128 = disk drive timed out db 7 dup(?) ; 40:42 ; Status bytes from NEC 765 ;---------------[Video display area]------------; db ? ; 40:49 ; Current CRT mode (software) ; 0 = 40 x 25 text (no color) ; 1 = 40 x 25 text (16 color) ; 2 = 80 x 25 text (no color) ; 3 = 80 x 25 text (16 color) ; 4 = 320 x 200 grafix 4 color ; 5 = 320 x 200 grafix 0 color ; 6 = 640 x 200 grafix 0 color ; 7 = 80 x 25 text (mono card) dw ? ; 40:4A ; Columns on CRT screen dw ? ; 40:4C ; Bytes in the regen region dw ? ; 40:4E ; Byte offset in regen region dw 8 dup(?) ; 40:50 ; Cursor pos for up to 8 pages dw ? ; 40:60 ; Current cursor mode setting db ? ; 40:62 ; Current page on display dw ? ; 40:63 ; Base addres (B000h or B800h) db ? ; 40:65 ; ic 6845 mode reg. (hardware) db ? ; 40:66 ; Current CGA palette ;---------------[Used to setup ROM]-------------; dw ?,? ; 40:67 ; Eprom base Offset,Segment db ? ; 40:6B ; Last spurious interrupt IRQ ;---------------[Timer data area]---------------; dw ? ; 40:6C ; Ticks since midnite (lo) dw ? ; 40:6E ; Ticks since midnite (hi) db ? ; 40:70 ; Non-zero if new day ;---------------[System data area]--------------; db ? ; 40:71 ; Sign bit set iff break dw ? ; 40:72 ; Warm boot iff 1234h value ;---------------[Hard disk scratchpad]----------; dw ?,? ; 40:74 ; ;---------------[Timout areas/PRT/LPT]----------; db 4 dup(?) ; 40:78 ; Ticks for LPT 1-4 timeouts db 4 dup(?) ; 40:7C ; Ticks for COM 1-4 timeouts ;---------------[Keyboard buf start/nd]---------; dw ? ; 40:80 ; Contains 1Eh, buffer start dw ? ; 40:82 ; Contains 3Eh, buffer end data ENDS dosdir SEGMENT at 50h ; Boot disk directory from IPL xerox label byte ; 0 if Print Screen idle ; 1 if PrtSc xeroxing screen ;255 if PrtSc error in xerox ; ...non-grafix PrtSc in bios db 200h dup(?) ; PC-DOS bootstrap procedure ; ...IBMBIO.COM buffers the ; ...directory of the boot ; ...device here at IPL time ; ...when locating the guts ; ...of the operating system ; ...filename "IBMDOS.COM" dosdir ends dosseg SEGMENT at 70h ; "Kernel" of PC-DOS op sys ;IBMBIO.COM file loaded by boot block. Device Drivers/Bootstrap. CONTIGUOUS<--- ;IBMDOS.COM operating system nucleus immediately follows IBMBIO.COM and | ; doesn`t have to be contiguous. The IBMDOS operating system nucleus | ; binary image is loaded by transient code in IBMBIO binary image | dosseg ends ; | iplseg SEGMENT at 0h ; Segment for boot block | ;The following boot block is loaded with 512. bytes on the first sector of | ;the bootable device by code resident in the ROM-resident bios. Control is | ;then transferred to the first word 0000:7C00 of the disk-resident bootstrap | ORG 07C00h ; ..offset for boot block | boot db 200h dup(?) ; ..start disk resident boot-- iplseg ends code SEGMENT ORG 0E000h BANNER db ' Generic Turbo XT Bios 1987',CR,LF db ' for 8088 or V20 cpu',CR,LF db ' (c)Anonymous',CR,LF db LF,0 LPTRS dw 03BCh,0378h,0278h ; Possible line printer ports ENTRY 0E05Bh ; IBM restart entry point COLD: MOV AX,40h ; Entered by POWER_ON/RESET MOV DS,AX MOV Word ptr DS:72h,0 ; Show data areas not init WARM: CLI ; Begin FLAG test of CPU XOR AX,AX JB HALT JO HALT JS HALT JNZ HALT JPO HALT ADD AX,1 JZ HALT JPE HALT SUB AX,8002h JS HALT INC AX JNO HALT SHL AX,1 JNB HALT JNZ HALT SHL AX,1 JB HALT MOV BX,0101010101010101b ; Begin REGISTER test of CPU CPUTST: MOV BP,BX MOV CX,BP MOV SP,CX MOV DX,SP MOV SS,DX MOV SI,SS MOV ES,SI MOV DI,ES MOV DS,DI MOV AX,DS CMP AX,0101010101010101b JNZ CPU1 NOT AX MOV BX,AX JMP CPUTST CPU1: XOR AX,1010101010101010b JZ CPU_OK HALT: HLT CPU_OK: CLD MOV AL,0 ; Prepare to initialize OUT 0A0h,AL ; ...no NMI interrupts MOV DX,3D8h ; Load Color Graphic port OUT DX,AL ; ...no video display MOV DX,3B8h ; Load Monochrome port INC AL ; ...no video display OUT DX,AL ; ...write it out MOV AL,10011001b ; Program 8255 PIA chip OUT 63h,AL ; ...Ports A & C, inputs MOV AL,10100101b ; Set (non)turbo mode OUT 61h,AL ; ...on main board MOV AL,01010100b ; ic 8253 inits memory refresh OUT 43h,AL ; ...chan 1 pulses ic 8237 to MOV AL,12h ; ...dma every 12h clock ticks OUT 41h,AL ; ...64K done in 1 millisecond MOV AL,01000000b ; Latch value 12h in 8253 clock OUT 43h,AL ; ...chip channel 1 counter IC8237: MOV AL,0 ; Do some initialization OUT 81h,AL ; ...dma page reg, chan 2 OUT 82h,AL ; ...dma page reg, chan 3 OUT 83h,AL ; ...dma page reg, chan 0,1 OUT 0Dh,AL ; Stop DMA on 8237 chip MOV AL,01011000b ; Refresh auto-init dummy read OUT 0Bh,AL ; ...on channel 0 of DMA chip MOV AL,01000001b ; Block verify OUT 0Bh,AL ; ...on channel 1 of DMA chip MOV AL,01000010b ; Block verify OUT 0Bh,AL ; ...on channel 2 of DMA chip MOV AL,01000011b ; Block verify OUT 0Bh,AL ; ...on channel 3 of DMA chip MOV AL,0FFh ; Refresh byte count OUT 1,AL ; ...send lo order OUT 1,AL ; ...send hi order MOV AL,0 ; Initialize 8237 command reg OUT 8,AL ; ...with zero OUT 0Ah,AL ; Enable DMA on all channels MOV AL,00110110b ; Set up 8253 timer chip OUT 43h,AL ; ...chan 0 is time of day MOV AL,0 ; Request a divide by OUT 40h,AL ; ...65536 decimal OUT 40h,AL ; ...0000h or 18.2 tick/sec MOV DX,213h ; Expansion unit port MOV AL,1 ; ...enable it OUT DX,AL ; ...do the enable MOV AX,40h ; Get bios impure segment MOV DS,AX ; ...into DS register MOV SI,DS:72h ; Save reset flag in SI reg XOR AX,AX ; ...cause memory check MOV BP,AX ; ...will clobber the flag MOV BX,AX ; Start at segment 0000h MOV DX,55AAh ; ...get pattern CLD ; Strings auto-increment MEMSIZ: XOR DI,DI ; Location XXXX:0 MOV ES,BX ; ...load segment MOV ES:[DI],DX ; ...write pattern CMP DX,ES:[DI] ; ...compare JNZ MEM_ND ; ...failed, memory end MOV CX,2000h ; Else zero 16 kilobytes REPZ STOSW ; ...with instruction ADD BH,4 ; ...get next 16K bytes ifdef MAX_MEMORY CMP BH,MAX_MEMORY SHR 2 ; Found max legal user ram? else CMP BH,0A0h ; Found max legal IBM ram? endif JNZ MEMSIZ ; ...no, then check more MEM_ND: MOV DS:72h,SI ; Save pointer XOR AX,AX MOV ES,AX ; ES = vector segment MOV AX,80h MOV SS,AX ; Set up temporary stack at MOV SP,100h ; 0080:0100 for memory check PUSH BP PUSH BX MOV BP,2 CALL MEMTST ; Memory check ES:0 - ES:0400 POP AX MOV CL,6 SHR AX,CL MOV DS:13h,AX POP AX JNB MEM_01 OR AL,ER_MEM ; Show vector area bad MEM_01: MOV DS:15h,AL ; Save IPL error code XOR AX,AX PUSH AX PUSH AX PUSH AX PUSH AX PUSH AX MOV AX,30h ; Set up IBM-compatible stack MOV SS,AX ; ...segment 0030h MOV SP,100h ; ...offset 0100h PUSH DS MOV BX,0E000h ; Check BIOS eprom PUSH CS POP DS ; ...at F000:E000 MOV AH,1 CALL CHKSUM ; ...for valid checksum POP DS ; ...restore impure<-DS JZ IC8259 OR Byte ptr DS:15h,ER_BIOS ; Checksum error BIOS eprom IC8259: CLI ; Init interrupt controller MOV AL,13h OUT 20h,AL MOV AL,8 OUT 21h,AL MOV AL,9 OUT 21h,AL MOV AL,0FFh OUT 21h,AL PUSH DS XOR AX,AX ; 8 nonsense vectors begin table MOV ES,AX ; ...at segment 0000h PUSH CS POP DS MOV CX,8 ; Vectors 00h - 07h unused XOR DI,DI ; ...we start at vec 00h LO_VEC: MOV AX,offset IGNORE ; Nonsense interrupt from RSX STOSW MOV AX,CS ; ...bios ROM segment STOSW LOOP LO_VEC MOV SI,offset VECTORS ; SI --> Vector address table MOV CX,18h ; ... vectors 08h - 1Fh busy HI_VEC: MOVSW ; Get INTERRUPT bios ROM offset MOV AX,CS STOSW ; ...INTERRUPT bios ROM segment LOOP HI_VEC MOV AX,0F600h ; AX --> Rom basic segment MOV DS,AX ; DS --> " " " XOR BX,BX ; BX = Rom basic offset MOV AH,4 ; Four basic roms to check MOV BP,SP ; Save the stack pointer PUSH CS ; ...push code segment MOV DX,offset SKIP ; Save the code offset PUSH DX ; ...for RAM_PATCH subroutine MOV DX,0EA90h ; Mov DX,'NOP,JMP_FAR' PUSH DX ; ...save it on stack MOV DX,0178Bh ; Mov DX,'MOV DX,[BX]' PUSH DX ; ...save it on stack PUSH SS ; Save stack segment MOV DX,SP ; ...get the stack offset ADD DX,02h ; ...calculate xfer addr. PUSH DX ; ...save it on the stack ; RETF ; Test for BASIC rom ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; MOV DX,[BX] ; Executes off the stack ; ; JMPF 0F000h,SKIP ; ...in RAM space ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SKIP: MOV SP,BP ; Restore the stack pointer CMP DL,DH ; ...compare 1st and 2nd byte JE kosher ; ...perfection. No piracy B_ROM: CALL CHKSUM ; Scan for BASIC roms JNZ kosher ; ...bad basic rom DEC AH ; Continue JNZ B_ROM ; ...yes, more POP DS ; Else valid basic MOV DI,60h ; ...install basic XOR AX,AX ; ...zero BASIC interrupt STOSW ; ...offset MOV AX,0F600h ; ...F600h BASIC interrupt STOSW ; ...segment PUSH DS kosher: POP DS ; Setup special low vectors MOV Word ptr ES:8,offset int_2 ; ...NMI interrupt MOV Word ptr ES:14h,offset int_5 ; ...print screen interrupt MOV Word ptr ES:7Ch,0 ; No special graphics chars. MOV Word ptr ES:7Eh,0 ; ...so zero vector 1Fh MOV DX,61h IN AL,DX ; Read machine flags OR AL,00110000b ; ...clear old parity error OUT DX,AL ; Write them back to reset AND AL,11001111b ; ...enable parity OUT DX,AL ; Write back, parity enabled MOV AL,80h ; ...allow NMI interrupts OUT 0A0h,AL MOV AX,0000000000110000b ; Assume monochrome video MOV DS:10h,AX ; ...card has been installed INT 10h ; ...initialize if present MOV AX,0000000000100000b ; Assume color/graphics video MOV DS:10h,AX ; ...card has been installed INT 10h ; ...initialize if present IN AL,62h ; Get memory size (64K bytes) AND AL,00001111b ; ...in bits 2,3 lo nibble MOV AH,AL ; Save memory size nibble MOV AL,10101101b OUT 61h,AL IN AL,62h ; Get no. of floppies (0-3) MOV CL,4 ; ...and init. video mode SHL AL,CL ; ...shift in hi nibble OR AL,AH MOV AH,0 MOV DS:10h,AX ; Start building Equipment Flag AND AL,00110000b ; ...if video card, mode set JNZ LE232 ; ...found video interface MOV AX,offset DUMMY ; No hardware, DUMMY: becomes MOV ES:40h,AX ; ...INT_10 video service JMP short LE235 LE232: CALL V_INIT ; Setup video LE235: MOV AL,00001000b ; Read low switches OUT 61h,AL MOV CX,2956h WAIT_1: LOOP WAIT_1 MOV AL,11001000b ; Keyboard acknowledge OUT 61h,AL ; ...send the request XOR AL,10000000b ; Toggle to enable OUT 61h,AL ; ...send key enable MOV AX,1Eh ; Offset to buffer start MOV DS:1Ah,AX ; Buffer head pointer MOV DS:1Ch,AX ; Buffer tail pointer MOV DS:80h,AX ; Buffer start ADD AX,20h ; ...size MOV DS:82h,AX ; Buffer end JMP short V_CONT FAO: MOV DL,AL ; Formatted ascii output FAO_1: MOV AX,BX ; Get position for CALL LOCATE ; ...cursor routine PUSH SI ; Get string address CALL PRINT ; ...print string MOV AX,ES:[BP+0] ; Get port # to print CALL BIGNUM ; ...four digits POP SI ; Restore string address INC BP ; ...Address of port INC BP ; ...is two bytes long INC BH ; ...down one line DEC DL ; Decrement device count JNZ FAO_1 ; ...back for more RET K_BYTE: CLC ; Say no error MOV AL,DL ; ...size "checked" INC AL ; ...show more DAA MOV DL,AL JNB KBY_01 MOV AL,DH ; ...do carry ADC AL,0 DAA MOV DH,AL KBY_01: MOV AL,DH CALL DIGIT ; Print hex digit MOV AL,DL MOV CL,4 ROR AL,CL CALL DIGIT ; Print hex digit MOV AL,DL CALL DIGIT ; Print hex digit RET TIMER: MOV DX,241h ; Check for timer #2 port CLI IN AL,DX ; ..read BCD seconds/100 STI CMP AL,99h ; Are BCD digits in range? JBE SER_01 ; ...yes, port exists ; MOV DX,341h ; Check for timer #1 port CLI IN AL,DX ; ..read BCD seconds/100 STI CMP AL,99h ; Are BCD digits in range? JBE SER_01 ; ...yes, port exists ; STC ; No hardware, ports 0FFh RET SER_01: CLC ; Found timer(s) answering RET V_CONT: MOV BP,4 ; Assume monochrome, 4K memory MOV BX,0B000h ; ...segment in BX MOV AL,DS:49h ; Get the video mode CMP AL,7 ; ...was it mono? JZ M_SEG ; ...yes, skip MOV BP,10h ; Else CGA, has 16K memory MOV BX,0B800h ; ...segment in BX M_SEG: PUSH BX ; Load video seg in ES POP ES MOV AL,DS:65h ; Get CRT hardware mode AND AL,11110111b ; ...disable video MOV DX,DS:63h ; Get 6845 index port ADD DX,4 ; ...add offset for OUT DX,AL ; 6845 controller port CRTRAM: CALL MEMTST ; Memory check ES:0 - ES:0400 DEC BP JNZ CRTRAM ; Loop until CRT RAM checked JNB LE2F5 OR Byte ptr DS:15h,ER_CRT ; Set CRT RAM error in status LE2F5: CALL V_INIT MOV AX,1414h ; Time-out value seconds MOV DS:78h,AX ; ...LPT1 MOV DS:7Ah,AX ; ...LPT2 MOV AX,101h ; Time-out value seconds MOV DS:7Ch,AX ; ...COM1 MOV DS:7Eh,AX ; ...COM2 MOV SI,offset LPTRS ; SI --> LPTR port table XOR DI,DI ; ...offset into data seg MOV CX,3 ; ...number of printers NXTPRT: MOV DX,CS:[SI] ; Get LPTR port MOV AL,10101010b ; ...write value OUT DX,AL ; ...to the LPTR MOV AL,11111111b ; Dummy data value OUT 0C0h,AL ; ...on the bus IN AL,DX ; Read code back CMP AL,10101010b ; ...check code JNZ NO_LPT ; ...no printer found MOV [DI+8],DX ; Save printer port INC DI INC DI NO_LPT: INC SI INC SI LOOP NXTPRT MOV AX,DI ; Number of printers * 2 MOV CL,3 ; ...get shift count ROR AL,CL ; ...divide by eight MOV DS:11h,AL ; ...save in equip. flag XOR DI,DI ; com port(s) at 40:00 (hex) COM_1: MOV DX,3FBh ; COM #1 line control reg. MOV AL,00011010b ; ...7 bits, even parity OUT DX,AL ; Reset COM #1 line cont. reg MOV AL,11111111b ; ...noise pattern OUT 0C0h,AL ; Write pattern on data buss IN AL,DX ; ...read result from COM #1 CMP AL,00011010b ; Check if serial port exists JNZ COM_2 ; ...skip if no COM #1 port MOV Word ptr [DI],3F8h ; Else save port # in impure INC DI ; ...potential COM #2 port INC DI ; ...is at 40:02 (hex) COM_2: MOV DX,2FBh ; COM #2 line control reg MOV AL,00011010b ; ...7 bits, even parity OUT DX,AL ; Reset COM #2 line cont. reg MOV AL,11111111b ; ...noise pattern OUT 0C0h,AL ; Write pattern on data buss IN AL,DX ; ...read results from COM #2 CMP AL,00011010b ; Check if serial port exists JNZ COM_CT ; ...skip if no COM #2 port MOV word ptr [DI],2F8h ; Else save port # in impure INC DI ; ...total number of serial INC DI ; ...interfaces times two COM_CT: MOV AX,DI ; Get serial interface count OR DS:11h,AL ; ...equip. flag MOV DX,201h IN AL,DX ; Read game controller TEST AL,0Fh ; ...anything there? JNZ NOGAME ; ...yes, invalid OR Byte ptr DS:11h,00010000b ; Else game port present NOGAME: MOV DX,0C000h ; ROM segment start PUSH DS FNDROM: MOV DS,DX ; Load ROM segment XOR BX,BX ; ...ID offset MOV AX,[BX] ; Read the ROM id CMP AX,0AA55h JNZ NXTROM ; ...not valid ROM MOV AX,40h MOV ES,AX MOV AH,0 MOV AL,[BX+2] ; Get ROM size (bytes * 512) MOV CL,5 SHL AX,CL ; Now ROM size in segments ADD DX,AX ; ...add base segment MOV CL,4 SHL AX,CL ; ROM address in bytes MOV CX,AX ; ...checksum requires CX CALL CHK_01 ; Find ROM checksum JNZ BADROM ; ...bad ROM PUSH DX MOV Word ptr ES:67h,3 ; Offset for ROM being setup MOV ES:69h,DS ; Segment for ROM being setup CALL Dword ptr ES:67h ; ...call ROM initialization POP DX JMP short FND_01 BADROM: OR Byte ptr ES:15h,ER_ROM ; ROM present, bad checksum NXTROM: ADD DX,80h ; Segment for next ROM FND_01: CMP DX,0F600h ; End of ROM space JL FNDROM ; ...no, continue POP DS IN AL,21h ; Read ic 8259 interrupt mask AND AL,10111100b ; ...enable IRQ (0,1,6) ints OUT 21h,AL ; (tod_clock,key,floppy_disk) MOV AH,1 MOV CH,0F0h INT 10h ; Set cursor type CALL BLANK ; ...clear display PUSH DS PUSH CS POP DS POP ES TEST Byte ptr ES:10h,1 ; Floppy disk present? JZ FND_02 ; ...no CMP Word ptr ES:72h,1234h ; Bios setup before? JNZ CONFIG ; ...no FND_02: JMP RESET ; Else skip memory check CONFIG: MOV AX,41Ah ; Where to move cursor MOV SI,offset STUF ; ...equipment message CALL LOCATE ; ...position cursor CALL PRINT ; ...and print string MOV AX,51Bh ; New cursor position MOV SI,offset STUF_1 ; ...CR/LF CALL Locate ; ...position cursor CALL PRINT ; ...and print string TEST Byte ptr ES:15h,11111111b ; Any error so far? JZ VALID ; ...no, skip CALL PRINT ; Print string MOV AL,ES:15h ; ...get error number CALL NUMBER ; ...print hex value CALL PRINT ; ...print prompt MOV BL,4 ; ...long beep CALL BEEP CALL GETCH ; Wait for keypress PUSH AX ; ...save answer CALL OUTCHR ; ...echo answer POP AX ; ...get answer CMP AL,'Y' ; Was it "Y" JZ FND_02 ; ...ok, continue CMP AL,'y' ; Was it "y" JZ FND_02 ; ...ok, continue JMPF 0F000h,COLD ; Else cold reset VALID: MOV SI,offset STUF_2 ; No errors found, load banner CALL PRINT ; ...and print string MOV AX,81Eh ; Where to move cursor CALL LOCATE ; ...position cursor CALL PRINT ; ...and print string MOV AX,91Ch ; Where to move cursor CALL LOCATE ; ...position cursor MOV BL,17h ; Character count FENCE: MOV AL,'-' ; Load ascii minus CALL OUTCHR ; ...and print it DEC BL JNZ FENCE MOV AX,0A21h ; Where to move cursor CALL LOCATE ; ...position cursor MOV AL,ES:49h ; Get CRT mode CMP AL,7 JZ FEN_01 ; ...monochrome MOV SI,offset STUF_3 ; ...color/graphics FEN_01: CALL PRINT ; Print the string MOV BX,0B21h MOV AL,ES:11h ; Get equipment byte PUSH AX MOV CL,6 ROR AL,CL AND AL,3 ; Number of printers JZ FEN_02 MOV BP,8 MOV SI,offset STUF_4 CALL FAO ; Formatted ascii output FEN_02: POP AX ; Equipment byte restore MOV SI,offset STUF_5 ; ...game controller PUSH AX ; Save a copy of equip. byte TEST AL,00010000b JZ NO_TOY ; Jump if no game controller MOV AX,BX CALL LOCATE ; Position cursor CALL PRINT ; ...and print string INC BH ; ...scroll line NO_TOY: CALL TIMER ; Timer devices? JB NO_TIM ; ...skip if none MOV AX,BX CALL LOCATE ; Position cursor INC BH MOV SI,offset STUF_8 CALL PRINT NO_TIM: POP AX MOV SI,offset STUF_6 ROR AL,1 ; Check for COM port AND AL,3 JZ NO_COM ; ...skip if no com XOR BP,BP CALL FAO ; Formatted ascii output NO_COM: MOV AX,121Ch ; Where to position cursor CALL LOCATE ; ...position cursor MOV SI,offset STUF_7 ; Memory size string CALL PRINT ; ...print string PUSH ES MOV BP,ES:13h ; Memory size (1 K blocks) DEC BP DEC BP MOV SI,2 MOV DX,SI MOV AX,80h MOV ES,AX CUTE: MOV AX,122Bh ; Cursory check of memory CALL LOCATE ; ...position cursor CALL K_BYTE ; ...print size in K CALL MEMTST ; Memory check ES:0 - ES:0400 JB BADRAM ; ...bad RAM found (How ???) DEC BP JNZ CUTE POP ES RESET: MOV BL,2 ; Do a warm boot CALL BEEP ; ...short beep CALL BLANK ; ...clear display MOV Word ptr ES:72h,1234h ; Show cold start done MOV AH,1 MOV CX,607h ; Set underline cursor INT 10h MOV SI,offset BANNER ; Load banner address CALL PRINT ; ...and print string INT 19h ; Boot the machine BADRAM: POP ES OR Byte ptr ES:15h,ER_RAM ; Show "Bad Ram" error JMP CONFIG STUF db ' Generic Turbo XT Bios 1987',0 STUF_1 db CR,LF,0,'System error #',0,', Continue?',0 STUF_2 db ' ',0,'Interface card list',0,'Monochrome',0 STUF_3 db 'Color/Graphics',0 STUF_4 db 'Printer #',0 STUF_5 db 'Game controller',0 STUF_6 db 'Async. commu. #',0 STUF_7 db 'RAM Testing .. 000 KB',0 STUF_8 db 'Timer',0 ENTRY 0E600h ; Not necessary to IPL here.. IPL: STI ; Called to reboot computer XOR AX,AX MOV DS,AX MOV Word ptr DS:78h,offset INT_1E ; Get disk parameter table MOV DS:7Ah,CS ; ...save segment MOV AX,4 ; Try up to four times RETRY: PUSH AX ; Save retry count MOV AH,0 ; ...reset INT 13h ; ...floppy JB FAILED MOV AL,1 ; One sector MOV AH,2 ; ...read XOR DX,DX ; ...from drive 0, head 0 MOV ES,DX ; ...segment 0 MOV BX,7C00h ; ...offset 7C00 MOV CL,1 ; ...sector 1 MOV CH,0 ; ...track 0 INT 13h ; ...floppy JB FAILED JMPF 0000h,7C00h ; Call the boot block ; FAILED: POP AX ; Get retries DEC AL ; ...one less JNZ RETRY NODISK: OR AH,AH ; Disk present? JNZ DERROR ; ...yes CALL BLANK ; Clear display PUSH CS POP DS MOV SI,offset DSKMSG ; Load disk message CALL PRINT ; ...and print string CALL GETCH ; ...wait for keypress CALL BLANK ; ...clear display MOV AX,0FF04h ; Reset retry count JMP RETRY ; ...and retry DERROR: XOR AX,AX ; Error from NEC 765 MOV DS,AX LES AX,Dword ptr DS:60h ; ROM basic vector ES:AX MOV BX,ES ; ...get ROM basic segment CMP AX,0 MOV AX,0 JNZ NODISK ; No ROM basic found CMP BX,0F600h JNZ NODISK ; Invalid ROM basic segment INT 18h ; ...else call ROM basic DSKMSG db 'Insert diskette in DRIVE A.',CR,LF db ' Press any key.',0 ENTRY 0E6F2h ; IBM entry point for INT 19h INT_19: JMP IPL ; Warm boot ENTRY 0E729h ; IBM entry point for INT 14h BAUD dw 0417h ; 110 baud clock divisor dw 0300h ; 150 baud clock divisor dw 0180h ; 300 baud clock divisor dw 00C0h ; 600 baud clock divisor dw 0060h ; 1200 baud clock divisor dw 0030h ; 2400 baud clock divisor dw 0018h ; 4800 baud clock divisor dw 000Ch ; 9600 baud clock divisor INT_14: STI ; Serial com. RS232 services PUSH DS ; ...thru IC 8250 uart (ugh) PUSH DX ; ...DX = COM device (0 - 3) PUSH SI PUSH DI PUSH CX PUSH BX MOV BX,40h MOV DS,BX MOV DI,DX ; MOV BX,DX ; RS232 serial COM index (0-3) SHL BX,1 ; ...index by bytes MOV DX,[BX] ; Convert index to port number OR DX,DX ; ...by indexing 40:0 JZ COM_ND ; ...no such COM device, exit OR AH,AH ; Init on AH=0 JZ COMINI DEC AH JZ COMSND ; Send on AH=1 DEC AH JZ COMGET ; Rcvd on AH=2 DEC AH JZ COMSTS ; Stat on AH=3 COM_ND: POP BX ; End of COM service POP CX POP DI POP SI POP DX POP DS IRET COMINI: PUSH AX ; Init COM port. AL has data ; = (Word Length in Bits - 5) ; +(1 iff two stop bits) * 4 ; +(1 iff parity enable) * 8 ; +(1 iff parity even ) * 16 ; +(BAUD: select 0-7 ) * 32 MOV BL,AL ADD DX,3 ; Line Control Register (LCR) MOV AL,80h ; ...index RS232_BASE + 3 OUT DX,AL ; Tell LCR to set (latch) baud MOV CL,4 ROL BL,CL ; Baud rate selects by words AND BX,00001110b ; ...mask off extraneous MOV AX,Word ptr CS:[BX+BAUD] ; Clock divisor in AX SUB DX,3 ; Load in lo order baud rate OUT DX,AL ; ...index RS232_BASE + 0 INC DX ; Load in hi order baud rate MOV AL,AH OUT DX,AL ; ...index RS232_BASE + 1 POP AX INC DX ; Find Line Control Register INC DX ; ...index RS232_BASE + 3 AND AL,00011111b ; Mask out the baud rate OUT DX,AL ; ...set (censored) init stat MOV AL,0 DEC DX ; Interrupt Enable Reg. (IER) DEC DX ; ...index RS232_BASE + 1 OUT DX,AL ; Interrupt is disabled DEC DX JMP short COMSTS ; Return current status COMSND: PUSH AX ; Send AL thru COM port MOV AL,3 MOV BH,00110000b ;(Data Set Ready,Clear To Send) MOV BL,00100000b ; ..(Data Terminal Ready) wait CALL WAITFR ; Wait for transmitter to idle JNZ HUNG ; ...time-out error SUB DX,5 ; ...(xmit) index RS232_BASE POP CX ; Restore char to CL register MOV AL,CL ; ...get copy to load in uart OUT DX,AL ; ...transmit char to IC 8250 JMP COM_ND ; ...AH register has status HUNG: POP CX ; Transmit error, restore char MOV AL,CL ; ...in AL for compatibility ; ...fall thru to gen. error HUNGG: OR AH,80h ; Set error (=sign) bit in AH JMP COM_ND ; ...common exit COMGET: MOV AL,1 ; Get char. from COM port MOV BH,00100000b ; Wait on DSR (Data Set Ready) MOV BL,00000001b ; Wait on DTR (Data Term.Ready) CALL WAITFR ; ...wait for character JNZ HUNGG ; ...time-out error AND AH,00011110b ; Mask AH for error bits SUB DX,5 ; ...(rcvr) index RS232_BASE IN AL,DX ; Read the character JMP COM_ND ; ...AH register has status COMSTS: ADD DX,5 ; Calculate line control stat IN AL,DX ; ...index RS232_BASE + 5 MOV AH,AL ; ...save high order status INC DX ; Calculate modem stat. reg. IN AL,DX ; ...index RS232_BASE + 6 JMP COM_ND ; ...save low order status ;AX=(DEL Clear_To_Send) * 1 ; (DEL Data_Set_ready)* 2 ; (Trailing_Ring_Det.)* 4 ; (DEL Carrier_Detect)* 8 ; ( Clear_To_Send )* 16 ; ( Data_Set_Ready)* 32 ; ( Ring_Indicator)* 64 ; ( Carrier_Detect)* 128 ; ************** ; ( Char received)* 256 ; ( Char smothered)* 512 ; ( Parity error )* 1024 ; ( Framing error )* 2048 ; ( Break detected)* 4096 ; ( Able to xmit )* 8192 ; ( Transmit idle )*16384 ; ( Time out error)*32768 POLL: MOV BL,byte ptr [DI+7Ch] ; Wait on BH in status or error POLL_1: SUB CX,CX ; Outer delay loop POLL_2: IN AL,DX ; ... inner loop MOV AH,AL AND AL,BH ; And status with user BH mask CMP AL,BH JZ POLLXT ; ... jump if mask set LOOP POLL_2 ; Else try again DEC BL JNZ POLL_1 OR BH,BH ; Clear mask to show timeout POLLXT: RET ; Exit AH reg. Z flag status WAITFR: ADD DX,4 ; Reset the Modem Control Reg. OUT DX,AL ; ...index RS232_BASE + 4 INC DX ; Calculate Modem Status Reg. INC DX ; ...index RS232_BASE + 6 PUSH BX ; Save masks (BH=MSR,BL=LSR) CALL POLL ; ...wait on MSR modem status POP BX ; ...restore wait masks BH,BL JNZ WAITF1 ; ..."Error Somewhere" by DEC DEC DX ; Calculate Line Status Reg. MOV BH,BL ; ...index RS232_BASE + 5 CALL POLL ; ...wait on LSR line status WAITF1: RET ; Status in AH reg. and Z flag ENTRY 0E82Eh ; IBM entry, key bios service INT_16: STI ; Keyboard bios services PUSH DS PUSH BX MOV BX,40h MOV DS,BX ; Load work segment OR AH,AH JZ KPD_RD ; Read keyboard buffer, AH=0 DEC AH JZ KPD_WT ; Set Z if char ready, AH=1 DEC AH JZ KPD_SH ; Return shift in AL , AH=2 KPD_XT: POP BX ; Exit INT_16 keypad service POP DS IRET KPD_RD: CLI ; No interrupts, alters buffer MOV BX,DS:1Ah ; ...point to buffer head CMP BX,DS:1Ch ; If not equal to buffer tail JNZ KPD_R1 ; ...char waiting to be read STI ; Else allow interrupts JMP KPD_RD ; ...wait for him to type KPD_R1: MOV AX,[BX] ; Fetch the character INC BX ; ...point to next character INC BX ; ...char = scan code + shift MOV DS:1Ah,BX ; Save position in head CMP BX,DS:82h ; ...buffer overflowed? JNZ KPD_XT ; ...no, done MOV BX,DS:80h ; Else reset to point at start MOV DS:1Ah,BX ; ...and correct head position JMP KPD_XT KPD_WT: CLI ; No interrupts, critical code MOV BX,DS:1Ah ; ...point to buffer head CMP BX,DS:1Ch ; ...equal buffer tail? MOV AX,[BX] ; (fetch, look ahead) STI ; Enable interrupts POP BX POP DS RETF 2 ; Do IRET, preserve flags KPD_SH: MOV AL,DS:17h ; Read keypad shift status JMP KPD_XT ENTRY 0E885h ; Align INT_9 at correct place ASCII db 000h,037h,02Eh,020h ; Scan -> Ascii. Sign bit set db 02Fh,030h,031h,021h ; ...if further work needed db 032h,033h,034h,035h db 022h,036h,038h,03Eh db 011h,017h,005h,012h db 014h,019h,015h,009h db 00Fh,010h,039h,03Ah db 03Bh,084h,001h,013h db 004h,006h,007h,008h db 00Ah,00Bh,00Ch,03Fh db 040h,041h,082h,03Ch db 01Ah,018h,003h,016h db 002h,00Eh,00Dh,042h db 043h,044h,081h,03Dh db 088h,02Dh,0C0h,023h db 024h,025h,026h,027h db 028h,029h,02Ah,02Bh db 02Ch,0A0h,090h NOALFA db 032h,036h,02Dh,0BBh ; Non-Alphabetic secondary db 0BCh,0BDh,0BEh,0BFh ; ...translation table db 0C0h,0C1h,0C2h,0C3h db 0C4h,020h,031h,033h db 034h,035h,037h,038h db 039h,030h,03Dh,01Bh db 008h,05Bh,05Dh,00Dh db 05Ch,02Ah,009h,03Bh db 027h,060h,02Ch,02Eh db 02Fh CTRLUP db 040h,05Eh,05Fh,0D4h ; CTRL uppercase secondary db 0D5h,0D6h,0D7h,0D8h ; ...translation table db 0D9h,0DAh,0DBh,0DCh ; ...for non-ASCII control db 0DDh,020h,021h,023h db 024h,025h,026h,02Ah db 028h,029h,02Bh,01Bh db 008h,07Bh,07Dh,00Dh db 07Ch,005h,08Fh,03Ah db 022h,07Eh,03Ch,03Eh db 03Fh CTRLLO db 003h,01Eh,01Fh,0DEh ; CTRL lowercase secondary db 0DFh,0E0h,0E1h,0E2h ; ...translation table db 0E3h,0E4h,0E5h,0E6h ; ...for non-ASCII control db 0E7h,020h,005h,005h db 005h,005h,005h,005h db 005h,005h,005h,01Bh db 07Fh,01Bh,01Dh,00Ah db 01Ch,0F2h,005h,005h db 005h,005h,005h,005h db 005h ALTKEY db 0F9h,0FDh,002h,0E8h ; ALT key secondary db 0E9h,0EAh,0EBh,0ECh ; ...translation table db 0EDh,0EEh,0EFh,0F0h db 0F1h,020h,0F8h,0FAh db 0FBh,0FCh,0FEh,0FFh db 000h,001h,003h,005h db 005h,005h,005h,005h db 005h,005h,005h,005h db 005h,005h,005h,005h db 005h NUMPAD db '789-456+1230.' ; Keypad secondary tralsator NUMCTR db 0F7h,005h,004h,005h ; Numeric keypad CTRL sec. db 0F3h,005h,0F4h,005h ; ...translation table db 0F5h,005h,0F6h,005h db 005h NUMUPP db 0C7h,0C8h,0C9h,02Dh ; Numeric keypad SHIFT sec. db 0CBh,005h,0CDh,02Bh ; ...translation table db 0CFh,0D0h,0D1h,0D2h db 0D3h INT_9: STI ; Key press hardware interrupt PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH DS PUSH ES CLD MOV AX,40h MOV DS,AX IN AL,60h ; Read the scan code data PUSH AX ; ...save it IN AL,61h ; Get control port status PUSH AX ; ...save it OR AL,10000000b ; Set "latch" bit to OUT 61h,AL ; ...acknowledge data POP AX ; Restore control status OUT 61h,AL ; ...to enable keyboard POP AX ; ...restore scan code MOV AH,AL ; Save copy of scan code CMP AL,11111111b ; ...check for overrun JNZ KY_01 ; ...no, OK JMP KY_BEP ; Else beep bell on overrun KY_EOI: MOV AL,20h ; Send end_of_interrupt code OUT 20h,AL ; ...to 8259 interrupt chip KY_XIT: POP ES ; Exit the interrupt POP DS POP DI POP SI POP DX POP CX POP BX POP AX IRET KY_01: AND AL,01111111b ; Valid scan code, no break CMP AL,46h JBE KY_02 JMP KY_CT8 KY_02: MOV BX,offset ASCII ; Table for ESC thru Scroll Lck XLAT CS:[BX] ; ...translate to Ascii OR AL,AL ; Sign flags "Shift" type key JS KY_FLG ; ...shift,caps,num,scroll etc OR AH,AH ; Invalid scan code? JS KY_EOI ; ...exit if so JMP short KY_ASC ; Else normal character KY_FLG: AND AL,01111111b ; Remove sign flag bit OR AH,AH ; ...check scan code JS KY_SUP ; ...negative, key released CMP AL,10h ; Is it a "toggle" type key? JNB KY_TOG ; ...yes OR DS:17h,AL ; Else set bit in "flag" byte JMP KY_EOI ; ...and exit KY_TOG: TEST Byte ptr DS:17h,00000100b ; Control key pressed? JNZ KY_ASC ; ...yes, skip TEST AL,DS:18h ; Else check "CAPS, NUM, SCRL" JNZ KY_EOI ; ...set, invalid, exit OR DS:18h,AL ; Show set in "flag_1" byte XOR DS:17h,AL ; ...flip bits in "flag" byte JMP KY_EOI KY_SUP: CMP AL,10h ; Released - is it "toggle" key JNB KY_TUP ; ...skip if so NOT AL ; Else form two's complement AND DS:17h,AL ; ...to do BIT_CLEAR "flags" CMP AL,11110111b ; ALT key release special case JNZ KY_EOI ; ...no, exit MOV AL,DS:19h ; Else get ALT-keypad character MOV AH,0 ; ...pretend null scan code MOV DS:19h,AH ; ...zero ALT-keypad character CMP AL,AH ; Was there a valid ALT-keypad? JZ KY_EOI ; ...no, ignore, exit JMP KY_NUL ; Else stuff it in ASCII buffer KY_TUP: NOT AL ; Form complement of toggle key AND DS:18h,AL ; ...to do BIT_CLEAR "flag_1" JMP KY_EOI KY_ASC: TEST Byte ptr DS:18h,00001000b ; Scroll lock pressed? JZ KY_NLK ; ...no CMP AH,45h ; Is this a NUM LOCK character? JZ KY_03 ; ...no AND Byte ptr DS:18h,11110111b ; Else clear bits in "flag_1" KY_03: JMP KY_EOI ; ...and exit KY_NLK: TEST Byte ptr DS:17h,00001000b ; ALT key pressed? JNZ KY_ALT ; ...yes TEST Byte ptr DS:17h,00000100b ; CTRL key pressed? JNZ KY_CTL ; ...yes TEST Byte ptr DS:17h,00000011b ; Either shift key pressed? JNZ KSHIFT ; ...yes KY_LC: CMP AL,1Ah ; Alphabetic character? JA KY_LC1 ; ...no ADD AL,'a'-1 ; Else add lower case base JMP KY_COM KY_LC1: MOV BX,offset NOALFA ; Non-alphabetic character SUB AL,20h XLAT CS:[BX] ; ...do the xlate JMP KY_COM KY_ALT: CMP AL,1Ah ; Control key pressed? JA KY_AGN ; ...no, skip MOV AL,0 ; Else illegal key press JMP KY_BFR KY_AGN: MOV BX,offset ALTKEY ; Load ALT key translation SUB AL,20h ; ...bias to printing char. XLAT CS:[BX] ; ...do the translation JMP KY_COM KY_CTL: CMP AH,46h ; Scroll lock key? JNZ KY_CT1 ; ...no, skip MOV Byte ptr DS:71h,10000000b ; Else CTRL-"Scroll" = break MOV AX,DS:80h ; ...get key buffer start MOV DS:1Ch,AX ; ...get key tail to start MOV DS:1Ah,AX ; ...get key head to start INT 1Bh ; Issue a "Break" interrupt SUB AX,AX JMP KY_CO2 KY_CT1: CMP AH,45h ; Num lock key? JNZ KY_CT2 ; ...no, skip OR Byte ptr DS:18h,00001000b ; Else show scroll lock MOV AL,20h ; ...send end_of_interrupt OUT 20h,AL ; ...to 8259 int. controller CMP Byte ptr DS:49h,7 ; Monochrome monitor? JZ KY_POL ; ...yes, skip MOV DX,3D8h ; Else reset mode MOV AL,DS:65h ; ...for the OUT DX,AL ; ...CGA color card KY_POL: TEST Byte ptr DS:18h,00001000b ; Wait for him to type JNZ KY_POL ; ...not yet JMP KY_XIT KY_CT2: CMP AH,3 ; Is it a Control @ (null) ? JNZ KY_CT3 ; ...no MOV AL,0 ; Else force a null KY_CT4: JMP KY_BFR ; ...save in buffer KY_CT3: CMP AL,1Ah ; Is it a control character? JBE KY_CT4 ; ...yes MOV BX,offset CTRLLO ; Else non-ascii control SUB AL,20h ; ...lower case XLAT CS:[BX] ; ...translation JMP KY_COM KSHIFT: CMP AH,37h ; Print_Screen pressed? JNZ KY_CT5 MOV AL,20h ; Yes, send end_of_interrupt OUT 20h,AL ; ...to 8259 interrupt chip INT 5 ; Request print_screen service JMP KY_XIT ; ...and exit key service KY_CT5: CMP AL,1Ah ; Alphabetic char? JA KY_CT6 ; ...no ADD AL,'A'-1 ; Yes, add base for alphabet JMP KY_COM KY_CT6: MOV BX,offset CTRLUP ; Non-ascii control SUB AL,20h ; ...upper case XLAT CS:[BX] ; ...translation JMP KY_COM KY_CT8: SUB AL,47h ; Keypad key, convert origin MOV BL,DS:17h ; ...get "flag" byte TEST BL,00001000b ; Look for ALT keypad entry JNZ KB_NUM ; ...do special entry thing TEST BL,00000100b ; CTRL key pressed? JNZ KY_CTR ; ...skip if so TEST BL,00100000b ; Toggle "Num Lock" ? JZ KY_CT9 ; ...no, continue TEST BL,00000011b ; Shift keys hit? JNZ KY_CTA ; ...no, check "INS" JMP KY_CTD ; Else xlat keypad char. KY_CT9: TEST BL,00000011b ; Shift keys hit? JZ KY_CTA ; ...no, check "INS" key JMP KY_CTD ; Else xlat keypad char. KB_NUM: OR AH,AH ; ALT-keypad entry, scan code JS KY_EO1 ; ...out of range TEST Byte ptr DS:17h,00000100b ; Else check CTRL state JZ KY_PAD ; ...not pressed, ALT keypad KY_PAT: CMP AH,53h ; Patch for CTRL ALT - toggle JNZ KY_PA1 ; ...not a DEL (reset) MOV Word ptr DS:72h,1234h ; Ctrl-Alt-Del, set init flag JMP WARM ; ...do a warm reboot KY_PA1: CMP AH,4Ah ; Is it a keypad "-" ? JNZ KY_PAD ; ...no, skip PUSH AX PUSH BX PUSH CX IN AL,61h ; Read equipment flags XOR AL,00001100b ; ...toggle speed OUT 61h,AL ; Write new flags back MOV AH,1 ; Video func=Set cursor type MOV CX,607h ; ...start at 6, end at 7 AND AL,4 ; Is turbo mode set? JZ KY_CUR ; ...no, keep big cursor MOV CH,0 ; Else set tiny cursor KY_CUR: INT 10h ; Set cursor type service MOV BX,DS:80h ; ...get start of key buf MOV DS:1Ah,BX ; ...set head to start MOV DS:1Ch,BX ; ...set tail to start POP CX POP BX POP AX KY_PAD: MOV BX,offset NUMPAD ; Get keypad translation table XLAT CS:[BX] ; ...convert to number CMP AL,'0' ; Is it a valid ASCII digit? JB KY_EO1 ; ...no, ignore it SUB AL,30h ; Else convert to number MOV BL,AL ; ...save a copy MOV AL,DS:19h ; Get partial ALT-keypad sum MOV AH,0Ah ; ...times 10 (decimal) MUL AH ADD AL,BL ; Add in new digit to sum MOV DS:19h,AL ; ...save as new ALT entry KY_EO1: JMP KY_EOI ; End_of_interrupt, exit KY_CTR: OR AH,AH ; Key released? JS KY_EO1 ; ...ignore if so MOV BX,offset NUMCTR ; Else Numeric Keypad Control XLAT CS:[BX] ; ...secondary translate JMP short KY_COM ; ...and save it KY_CTA: CMP AH,0D2h ; Was "INS" key released? JNZ KY_CTB AND Byte ptr DS:18h,01111111b ; Yes, clear "INS" in "FLAG_1" JMP short KY_EO1 KY_CTB: OR AH,AH ; Key released? JS KY_EO1 ; ...ignore if so CMP AH,52h ; Else check for "INS" press JNZ KY_CTC ; ...not "INS" press TEST Byte ptr DS:18h,10000000b ; Was INS key in effect? JNZ KY_EO1 ; ...yes, ignore XOR Byte ptr DS:17h,10000000b ; Else tog "INS" in "FLAG" byte OR Byte ptr DS:18h,10000000b ; ...set "INS" in "FLAG_1" byte KY_CTC: MOV BX,offset NUMUPP ; Numeric Keypad Upper Case XLAT CS:[BX] ; ...secondary translation JMP short KY_COM KY_CTD: OR AH,AH ; Was the key released? JS KY_EO1 ; ...yes, ignore MOV BX,offset NUMPAD ; Load translation table XLAT CS:[BX] ; ...do translate JMP short KY_COM KY_COM: CMP AL,5 ; Common entry, char in AL JZ KY_EO2 ; ...Control E, ignore CMP AL,4 JA KY_CO1 ; Above Control D OR AL,10000000b ; Else set sign flag JMP short KY_CO2 KY_CO1: TEST AL,10000000b ; Is sign bit set? JZ KY_CO3 ; ...skip if so AND AL,01111111b ; Else mask sign off KY_CO2: MOV AH,AL ; Save in high order byte MOV AL,0 ; ...set scan code to zero KY_CO3: TEST Byte ptr DS:17h,01000000b ; Test for "CAPS LOCK" state JZ KY_BFR ; ...no, skip TEST Byte ptr DS:17h,00000011b ; Test for SHIFT key JZ KY_CO4 ; ...skip if no shift CMP AL,'A' ; Check for alphabetic key JB KY_BFR ; ...not SHIFT_able CMP AL,'Z' ; Check for alphabetic key JA KY_BFR ; ...not SHIFT_able ADD AL,20h ; Else do the shift JMP short KY_BFR KY_CO4: CMP AL,'a' ; Check for alphabetic key JB KY_BFR ; ...not SHIFT_able CMP AL,'z' ; Check for Alphabetic key JA KY_BFR ; ...not SHIFT_able SUB AL,20h ; Else do the shift KY_BFR: MOV BX,DS:1Ch ; BX = tail of buffer MOV DI,BX ; ...save it INC BX ; ...advance INC BX ; ...by word CMP BX,DS:82h ; End of buffer reached? JNZ KY_CHK ; ...no, skip MOV BX,DS:80h ; Else BX = beginning of buffer KY_CHK: CMP BX,DS:1Ah ; BX = Buffer Head ? JNZ KY_STF ; ...no, OK JMP short KY_BEP ; Else buffer overrun, beep KY_STF: MOV [DI],AX ; Stuff scan code, char in bfr MOV DS:1Ch,BX ; ...and update bfr tail KY_EO2: JMP KY_EOI KY_BEP: MOV AL,20h ; Keyboard beeper routine OUT 20h,AL ; ...send end_of_interrupt MOV BX,80h ; Cycles in beep IN AL,61h ; ...get status PUSH AX ; ...save copy KY_BE1: AND AL,11111100b ; Mask off speaker bits OUT 61h,AL ; ...disable speaker KY_BE2: MOV CX,64h ; Constant for pitch KY_BE3: LOOP KY_BE3 ; ...delay, speaker off XOR AL,00000010b OUT 61h,AL ; Toggle speaker position TEST AL,00000010b ; Full cycle done yet? JZ KY_BE2 ; ...no, do other half cycle DEC BX ; Else show cycle sent JNZ KY_BE1 ; ...more cycles to send POP AX OUT 61h,AL ; Restore flags MOV CX,32h ; Silence counter KY_BE4: LOOP KY_BE4 ; Send nothing for while JMP KY_XIT KY_NUL: MOV AH,38h ; ALT key pressed, released JMP KY_BFR ; ...for no logical reason ENTRY 0EC59h ; IBM entry point for floppy INT_13: STI ; Floppy disk services PUSH BP PUSH SI PUSH DI PUSH DS PUSH ES PUSH BX MOV DI,AX ; Request type in DI, for index XOR AX,AX MOV DS,AX LES SI,Dword ptr DS:78h ; Get disk parameter table MOV AX,40h MOV DS,AX MOV BX,5 MOV AX,ES:[BX+SI] ; Get (Gap Length, DTL) in AX PUSH AX ; ...save it DEC BX DEC BX MOV AX,ES:[BX+SI] ; Get (Bytes/sector,EOT) in AX PUSH AX ; ...save it XCHG CL,DH XCHG DL,CL PUSH DX ; Push (Head,Drive) swapped PUSH CX PUSH DI MOV BP,SP ; Mark bottom of stack frame ifdef SLOW_FLOPPY CALL FD_SPD ; ...execute request lo speed else CALL FD_XQT ; ...execute at current speed endif MOV AH,ES:[SI+2] ; Get new motor count MOV DS:40h,AH ; ...and save it MOV AH,DS:41h ; Get completion status CMP AH,1 ; ...check for write protect CMC ; ...was write protect error POP BX POP CX POP DX XCHG DL,CL XCHG CL,DH POP BX ; Clean POP BX ; ...up POP BX ; ...stack POP ES POP DS POP DI POP SI POP BP RETF 2 FD_XQT: MOV AL,[BP+1] ; Get floppy service number OR AL,AL JZ FD_RST ; ...reset, AH=0 DEC AL JZ FD_XQ3 ; ...read status, AH=1 CMP Byte ptr [BP+2],3 ; For track number above 3? JA FD_XQ1 ; ...yes CMP AL,5 ; Service within range? JBE FD_XQ2 ; ...yes FD_XQ1: MOV Byte ptr DS:41h,1 ; Say write protect error RET FD_XQ2: JMP FD_001 ; Execute legal service FD_XQ3: MOV AL,DS:41h ; Return NEC status byte RET FD_RST: MOV DX,3F2h ; Reset the floppy disk system CLI AND Byte ptr DS:3Fh,00001111b ; Clear "write in progress" MOV AL,DS:3Fh ; ...find out busy drives MOV CL,4 SHL AL,CL TEST AL,00100000b JNZ FD_RS1 ; Drive #1 active TEST AL,01000000b JNZ FD_RS2 ; Drive #2 active TEST AL,10000000b JZ FD_RS0 ; Drive #3 idle FD_RS3: INC AL FD_RS2: INC AL FD_RS1: INC AL FD_RS0: MOV Byte ptr DS:3Eh,0 ; All drives need recalibrate MOV Byte ptr DS:41h,0 ; ...no completion status OR AL,00001000b ; Interrupt ON in command word OUT DX,AL ; ...send word to controller OR AL,00000100b ; "Reset" in command word OUT DX,AL ; ...send word to controller STI CALL NC_BSY ; Wait for completion CALL NC_STS ; ...read result block MOV AL,DS:42h CMP AL,0C0h ; Did the reset work JZ FD_RS4 ; ...yes MOV Byte ptr DS:41h,20h ; Else set controller error JMP short FD_RS5 ; ...return FD_RS4: MOV AL,3 ; Specify command to NEC CALL NEC765 ; ...send it MOV AL,ES:[SI] ; First byte in param block CALL NEC765 ; ...send it MOV AL,ES:[SI+1] ; Secnd byte in param block CALL NEC765 ; ...send it FD_RS5: RET NECFUN db 003h,000h,0E6h,0C5h,0E6h,04Dh ; NEC function table lookup NECDMA db 000h,000h,046h,04Ah,042h,04Ah ; DMA modes for 8237 NECWRT db 000h,000h,000h,080h,000h,080h ; Write flag table lookup NECDRV db 1,2,4,8 ; Drive number table lookup NECERR db 80h,20h,10h,4,2,1 ; Error code table lookup NECSTS db 04h,10h,08h,04h,03h,02h,20h ; Disk status table lookup FD_001: CLI ; Normal (non-reset) commands MOV Byte ptr DS:41h,0 ; ...reset status MOV AL,[BP+1] ; Get command word MOV AH,0 MOV DI,AX ; Save copy, zero-extended OUT 0Ch,AL ; ...diddle LSB/MSB flip-flop MOV AL,CS:[DI+NECDMA] ; Fetch DMA mode OUT 0Bh,AL ; ...send it to IC8237 MOV AX,[BP+0Ch] ; Get segment address MOV CL,4 ; ...convert ROL AX,CL ; ...to (offset, 64K page no) MOV CH,AL ; Extract page number (0-15.) AND CH,00001111b ; ...for 8237 dma controller AND AL,11110000b ; Extract implicit page offset ADD AX,[BP+0Ah] ; ...add explicit user offset ADC CH,0 ; ...(page number overflowed) MOV DX,AX ; Now save lo 16 bits of addr. OUT 4,AL ; ...send lowest 8 bits " " MOV AL,AH OUT 4,AL ; ...send next 8 bits " " MOV AL,CH OUT 81h,AL ; 64K page no to DMA page reg MOV AH,[BP+0] MOV AL,0 SHR AX,1 ; Sector cnt * 128 MOV CL,[BP+6] ; Track count SHL AX,CL ; * sector count DEC AX ; - 1 OUT 5,AL ; Send 1/2 of the word count XCHG AL,AH OUT 5,AL ; Send 2/2 of the word count XCHG AL,AH ADD AX,DX ; Compute final address JNB FD_002 ; ...ok STI MOV Byte ptr DS:41h,9h ; Else wrapped around 64K byte JMP FD_64K ; ...page register FD_002: MOV AL,2 ; Disable floppy disk dma OUT 0Ah,AL MOV Byte ptr DS:40h,0FFh ; Set large motor timeout MOV BL,[BP+2] ; ...get drive number MOV BH,0 MOV AL,CS:[BX+NECDRV] ; Table lookup bit position MOV CH,AL ; ...save mask MOV CL,4 SHL AL,CL ; Shift mask into place OR AL,BL ; ...or in drive select OR AL,0Ch ; ...or in DMA and NO RESET MOV DX,3F2h OUT DX,AL ; Send to floppy control port STI MOV AL,CS:[DI+NECWRT] ; Table lookup for write flag OR DS:3Fh,AL ; ...set write flag if active OR AL,AL JNS FD_003 ; ...skip if non-write MOV AH,ES:[SI+0Ah] ; Motor start from param blk OR AH,AH JZ FD_003 ; ...none specified TEST CH,DS:3Fh ; Was this drive motor running? JNZ FD_003 ; ...skip if so CALL FD_WT1 ; Else delay for motor start FD_003: OR DS:3Fh,CH ; Show this motor is running TEST CH,DS:3Eh ; Drive recalibration needed? JNZ FD_004 ; ...no, skip OR DS:3Eh,CH ; Else show recalibrated MOV AL,7 ; Send RECAL command CALL NEC765 ; ...to NEC 765 chip MOV AL,BL CALL NEC765 ; ...drive number CALL NC_BSY ; Wait for completion of RECAL CALL NEC_04 ; ...dummy call to RET FD_004: MOV AL,0Fh ; Request a seek CALL NEC765 ; ...from the NEC 765 MOV AL,BL CALL NEC765 ; Drive number MOV AL,[BP+3] CALL NEC765 ; Cylinder number CALL NC_BSY ; ...wait for completion CALL NC_STS ; ...read results MOV AL,ES:[SI+9] ; Get head settle time OR AL,AL ; ...none specified? JZ FD_005 ; ...if none, skip FD_STL: MOV CX,226h ; Delay time for head settle FD_STZ: LOOP FD_STZ ; ...timed wait DEC AL ; ...delay in millisec JNZ FD_STL ; ...wait some more FD_005: MOV AL,CS:[DI+NECFUN] ; Translate user service, then CALL NEC765 ; ...and send as NEC func MOV AL,[BP+4] ; AND AL,1 SHL AL,1 SHL AL,1 OR AL,BL CALL NEC765 CMP Byte ptr [BP+1],5 ; Is this a format request? JNZ FD_006 ; ...skip if not MOV AL,[BP+6] ; Else use user bytes/sector CALL NEC765 MOV AL,[BP+7] ; ... user EOT CALL NEC765 MOV AL,ES:[SI+7] ; Disk table format gap length CALL NEC765 MOV AL,ES:[SI+8] ; Disk table format fill byte CALL NEC765 JMP short FD_008 FD_006: MOV CX,7 ; Else lookup bytes * 512/sec MOV DI,3 ; ...from disk table FD_007: MOV AL,[BP+DI] ; AL has bytes/sector * 512 CALL NEC765 INC DI ; ...get next item for table LOOP FD_007 ; ...also (EOT,GAP,DTL...) FD_008: CALL NC_BSY ; Wait on floppy i/o completion CALL NC_ST1 ; ...get NEC status MOV AL,DS:42h ; ...into AL AND AL,11000000b ; Isolate errors JZ FD_012 ; ...no errors CMP AL,40h ; Test direction bit JZ FD_ERR MOV Byte ptr DS:41h,20h ; Set if bad controller JMP short FD_012 ; ...return error FD_ERR: MOV AL,DS:43h ; Read return code from block MOV CX,6 ; ...number of error types XOR BX,BX ; Start at error type 0 FD_009: TEST AL,CS:[BX+NECERR] ; Has error type BX occured? JNZ FD_010 ; ...yes INC BX ; Else try next error type LOOP FD_009 ; ...until done FD_010: MOV AL,CS:[BX+NECSTS] ; Translate error code again MOV DS:41h,AL ; ...store it as disk status FD_012: MOV AL,DS:45h ; Get bytes read CMP AL,[BP+3] ; ...compare with requested MOV AL,DS:47h ; Read sectors requested JZ FD_013 ; ...return if all read MOV AL,[BP+7] ; Else read sectors requested INC AL ; ...add one for luck FD_013: SUB AL,[BP+5] ; Subtract stectors read RET FD_64K: MOV AL,0 ; Overflowed 64K page boundary RET ; ...show no sectors read NC_BSY: STI ; Wait for operation to finish XOR CX,CX ; ...zero lo order delay MOV AL,2 ; Load hi order delay NC_BS1: TEST Byte ptr DS:3Eh,10000000b ; Has interrupt set the flag? CLC ; ...hack to slow CPU JNZ NC_BS2 ; ...yes LOOP NC_BS1 ; Else back for more DEC AL JNZ NC_BS1 MOV Byte ptr DS:41h,80h ; Time-out, say it completed POP AX MOV AL,0 ; ...return time out code STC ; ...error status RET NC_BS2: AND Byte ptr DS:3Eh,01111111b ; Mask off completion status RET ; ...return carry clear NC_RDY: PUSH CX ; Wait for NEC ready for comand XOR CX,CX MOV DX,3F4h ; ...NEC status port NC_RD1: IN AL,DX ; Read status of NEC 765 chip OR AL,AL JS NC_RD2 ; ...able to accept command LOOP NC_RD1 MOV Byte ptr DS:41h,80h ; Else show timeout error JMP short NC_RD3 NC_RD2: TEST AL,01000000b ; Test the direction bit JNZ NC_RD4 MOV Byte ptr DS:41h,20h ; ...clear iff controller err NC_RD3: POP CX STC RET NC_RD4: INC DX ; Load NEC data port IN AL,DX ; ...read it PUSH AX MOV CX,0Ah ; Short delay NC_RD5: LOOP NC_RD5 DEC DX ; Load NEC status port IN AL,DX ; ...read status TEST AL,00010000b ; ...set Z flag if done CLC ; ...return success POP AX POP CX RET FD_WT1: PUSH CX ; Millisecond delay in AH FD_WT2: XOR CX,CX FD_WT3: LOOP FD_WT3 DEC AH JNZ FD_WT2 POP CX RET ifdef SLOW_FLOPPY ; Run floppy at SLOWEST speed FD_SPD: IN AL,61h ; Toggle speed on Floppy Disk PUSH AX ; ...save old clock rate AND AL,11110011b ; ...load slowest clock rate OUT 61h,AL ; ...slow down to 4.77 mHz CALL FD_XQT ; Execute the i/o request POP AX ; ...restore old clock rate OUT 61h,AL ; ...from saved clock byte RET endif ENTRY 0EF57h ; Disk interrupt entry INT_E: STI ; Floppy disk attention PUSH DS PUSH AX MOV AX,40h MOV DS,AX OR Byte ptr DS:3Eh,10000000b ; Raise "attention" flag MOV AL,20h ; Send end_of_interrupt code OUT 20h,AL ; ...to 8259 interrupt chip POP AX POP DS IRET NC_STS: MOV AL,8 ; Send a "Request status" CALL NEC765 ; ...to the NEC 765 chip NC_ST1: PUSH BX ; Alternate entry point PUSH CX MOV CX,7 XOR BX,BX NC_ST2: CALL NC_RDY ; Wait for NEC 765 ready JB NC_ST3 ; ...NEC 765 error MOV [BX+42h],AL ; Save status in BIOS block JZ NC_ST4 ; ...NEC 765 ready INC BX ; Count more LOOP NC_ST2 MOV Byte ptr DS:41h,20h ; NEC 765 controller error NC_ST3: STC ; Set error condition POP CX POP BX POP AX MOV AL,0 RET NC_ST4: POP CX ; Successful return POP BX RET NEC765: PUSH CX ; Send control to NEC 765 chip PUSH DX PUSH AX XOR CX,CX MOV DX,3F4h ; Load NEC 765 status port NEC_01: IN AL,DX ; Read NEC 765 status OR AL,AL JS NEC_02 ; ...done LOOP NEC_01 MOV Byte ptr DS:41h,80h ; Set time out status JMP short NEC_05 NEC_02: TEST AL,40h ; Check data direction JZ NEC_03 MOV Byte ptr DS:41h,20h ; ...NEC 765 is gimped JMP short NEC_05 NEC_03: INC DX ; Load NEC 765 data port POP AX OUT DX,AL ; ...write user's parameter CLC POP DX POP CX NEC_04: RET NEC_05: POP AX ; Common error return POP DX POP CX POP AX MOV AL,0 STC RET ENTRY 0EFC7h ; IBM entry for disk param INT_1E: db 11001111b ; Disk parameter table db 2 db 25h db 2 db 8 db 2Ah db 0FFh db 50h db 0F6h db 19h db 4 ENTRY 0EFD2h ; IBM entry for parallel LPT INT_17: STI ; Parallel printer services PUSH DS PUSH BX PUSH CX PUSH DX MOV BX,40h MOV DS,BX MOV BX,DX ; DX is printer index (0 - 3) SHL BX,1 ; ...word index MOV DX,[BX+8] ; Load printer port OR DX,DX JZ LP_01 ; Goes to black hole OR AH,AH JZ LP_02 ; Function is print, AH=0 DEC AH JZ LP_INI ; Function is init , AH=1 DEC AH JZ LP_STS ; Get the status , AH=2 LP_01: POP DX POP CX POP BX POP DS IRET LP_02: OUT DX,AL ; Char --> data lines 0-7 INC DX ; Printer status port MOV BH,[BX+78h] ; Load time out parameter MOV AH,AL LP_05: XOR CX,CX ; Clear lo order time out LP_POL: IN AL,DX ; Get line printer status OR AL,AL ; ...ready? JS LP_DON ; ...done if so LOOP LP_POL DEC BH ; Decrement hi order time out JNZ LP_05 OR AL,00000001b ; Set timeout in Status Byte AND AL,11111001b ; ...bits returned to caller JMP short LP_TOG LP_DON: INC DX ; Printer control port MOV AL,00001101b ; Set output strobe hi OUT DX,AL ; ...data lines 0-7 valid LP_STR: MOV AL,00001100b ; Set output strobe lo OUT DX,AL ; ...data lines 0-7 ????? DEC DX ; Printer status port JMP short LP_ST1 ; ...get line printer status LP_STS: MOV AH,AL ; Save copy of character INC DX ; Printer status port LP_ST1: IN AL,DX ; Read printer status AND AL,11111000b ; ...bits returned to caller LP_TOG: XOR AL,01001000b ; ...toggle ERROR,ACKNOWLEDGE XCHG AL,AH JMP LP_01 ; Exit, AH=Status,AL=character LP_INI: MOV AH,AL ; Initialize the line printer INC DX INC DX MOV AL,00001000b OUT DX,AL ; Request initialize MOV CX,5DCh ; ...delay LP_DLY: LOOP LP_DLY JMP LP_STR ; Strobe the line printer ENTRY 0F045h ; IBM entry point for table V_TABLE dw CRT_0 ; Set mode dw CRT_1 ; Set cursor type dw CRT_2 ; Set cursor position dw CRT_3 ; Get cursor position dw CRT_4 ; Read light pen position dw CRT_5 ; Set active display page dw CRT_6 ; Scroll active page up dw CRT_7 ; Scroll active page down dw CRT_8 ; Read attribute/character dw CRT_9 ; Write attribute/character dw CRT_10 ; Read character only dw CRT_11 ; Set color dw CRT_12 ; Write pixel dw CRT_13 ; Read pixel dw CRT_14 ; Write teletype dw CRT_15 ; Return current video state ENTRY 0F065h ; IBM entry, video bios service INT_10: STI ; Video bios service AH=(0-15.) CLD ; ...strings auto-increment PUSH BP PUSH ES PUSH DS PUSH SI PUSH DI PUSH DX PUSH CX PUSH BX PUSH AX MOV BX,40h MOV DS,BX MOV BL,DS:10h ; Get equipment byte AND BL,00110000b ; ...isolate video mode CMP BL,00110000b ; Check for monochrome card MOV BX,0B800h JNZ C_01 ; ...not there, BX --> CGA MOV BX,0B000h ; Else BX --> MONO C_01: PUSH BX ; Save video buffer address MOV BP,SP ; ...start of stack frame CALL C_02 ; ...then do the function POP SI POP AX POP BX POP CX POP DX POP DI POP SI POP DS POP ES POP BP IRET MAPBYT: PUSH DX ; Mul AL by BX, CX --> buf MOV AH,0 MUL BX ; Position in AX POP DX MOV CX,[BP+0] ; CX --> video buffer RET ENTRY 0F0A4h ; IBM entry, SET_MODE tables INT_1D: db 38h,28h,2Dh,0Ah,1Fh,6,19h ; Init string for 40 x 25 db 1Ch,2,7,6,7 db 0,0,0,0 db 71h,50h,5Ah,0Ah,1Fh,6,19h ; Init string for 80 x 25 col db 1Ch,2,7,6,7 db 0,0,0,0 db 38h,28h,2Dh,0Ah,7Fh,6,64h ; Init string for GRAPHIX db 70h,2,1,6,7 db 0,0,0,0 db 61h,50h,52h,0Fh,19h,6,19h ; Init string for 80 x 25 b/w db 19h,2,0Dh,0Bh,0Ch db 0,0,0,0 REGENL dw 0800h ; Regen len, 40 x 25 dw 1000h ; 80 x 25 dw 4000h ; GRAPHIX dw 4000h MAXCOL db 28h,28h,50h,50h,28h,28h,50h,50h ; Maximum columns MODES db 2Ch,28h,2Dh,29h,2Ah,2Eh,1Eh,29h ; Table of mode sets TABMUL db 00h,00h,10h,10h,20h,20h,20h,30h ; Table lookup for multiply C_02: CMP AH,0Fh ; Is AH a legal video command? JBE C_03 RET ; ...error return if not C_03: SHL AH,1 ; Make word value MOV BL,AH ; ...then set up BX MOV BH,0 JMP Word ptr CS:[BX+V_TABLE] ; ...vector to routines CRT_0: MOV AL,DS:10h ; Set mode of CRT MOV DX,3B4h ; ...mono port AND AL,00110000b ; ...get display type CMP AL,00110000b ; ...equal if mono MOV AL,1 ; Assume mono display MOV BL,7 ; ...mode is 7 JZ C0_01 ; ...Skip if mono, else CGA MOV BL,[BP+2] ; BL = mode number (user AL) MOV DL,0D4h ; 3D4 is CGA port DEC AL C0_01: MOV DS:63h,DX ; Save cur. CRT display port ADD DL,4 OUT DX,AL ; Reset the video MOV DS:49h,BL ; ...save cur. CRT mode PUSH DS XOR AX,AX MOV DS,AX LES SI,Dword ptr DS:74h ; SI --> INT_1D video param POP DS MOV BH,0 PUSH BX MOV BL,CS:[BX+TABMUL] ; Get BL for index into INT_1D ADD SI,BX MOV CX,10h ; Sixteen values to send C0_02: MOV AL,ES:[SI] ; Value to send in SI CALL SENDAX ; ...send it INC AH ; ...bump count INC SI ; ...point to next LOOP C0_02 ; ...loop until done MOV BX,[BP+0] ; BX --> regen buffer MOV ES,BX ; ...into ES segment XOR DI,DI CALL MODCHK ; Set flags acc. to mode MOV CX,2000h ; ...assume CGA MOV AX,0 ; ...and graphics JB C0_04 ; ...do graphics fill JNZ C0_03 ; ...Alphanumeric fill MOV CX,800h ; ...mono card C0_03: MOV AX,7*100h+' ' ; Word for text fill C0_04: REPZ STOSW ; ...fill regen buffer MOV DX,DS:63h ; Get the port ADD DL,4 POP BX MOV AL,CS:[BX+MODES] ; Load data to set for mode OUT DX,AL ; ...and send it MOV DS:65h,AL ; ...then save active data INC DX MOV AL,30h ; Assume not 640 x 200 b/w CMP BL,6 ; ...correct? JNZ C0_05 MOV AL,3Fh ; Palette for 640 x 200 b/w C0_05: MOV DS:66h,AL ; ...save palette OUT DX,AL ; ...send palette XOR AX,AX MOV DS:4Eh,AX ; Start at beg. of 1st page MOV DS:62h,AL ; ...active page=page 0 MOV CX,8 ; Do 8 pages of cursor data MOV DI,50h ; Page cursor data at 40:50 C0_06: MOV [DI],AX ; Cursor at upper left of page INC DI ; ...next page LOOP C0_06 MOV Word ptr DS:60h,0607h ; Cursor: Line 6 thru Line 7 MOV AL,CS:[BX+MAXCOL] ; Get display width MOV DS:4Ah,AX ; ...save it AND BL,11111110b MOV AX,Word ptr CS:[BX+REGENL] ; Get video regen length MOV DS:4Ch,AX ; ...save it RET CRT_1: MOV CX,[BP+6] ; Set cursor type, from CX MOV DS:60h,CX ; ...save it MOV AH,0Ah ; CRT index register 0Ah CALL OT6845 ; ...send CH,CL to CRT reg RET CRT_2: MOV BL,[BP+5] ; Set cursor position, page BH SHL BL,1 ; ...(our BL) MOV BH,0 MOV AX,[BP+8] ; Position in user DX (our AX) MOV [BX+50h],AX ; ...remember cursor position JMP SETCUR ; ...set 6845 cursor hardware CRT_3: MOV BL,[BP+5] ; Get cursor position, page BH SHL BL,1 MOV BH,0 MOV AX,[BX+50h] MOV [BP+8],AX ; ...return position in user DX MOV AX,DS:60h ; Get cursor mode MOV [BP+6],AX ; ...return in user CX RET PENOFF: db 3,3,5,5,3,3,3,4 ; Light pen offset table CRT_4: MOV DX,DS:63h ; Read light pen position ADD DL,6 MOV Byte ptr [BP+3],0 ; AH=0, assume not triggered IN AL,DX TEST AL,00000100b JZ C4_05 ; Skip, reset if pen not set TEST AL,00000010b JNZ C4_01 ; Skip if pen triggered RET ; ...return, do not reset C4_01: MOV AH,10h ; Offset to pen port is 10h CALL PENXY ; ...read into CH,CL MOV BL,DS:49h ; Get CRT mode data word MOV CL,BL MOV BH,0 MOV BL,Byte ptr CS:[BX+PENOFF] ; Load offset for subtraction SUB CX,BX JNS C4_02 ; ...did not overflow XOR AX,AX ; Else fudge a zero C4_02: CALL MODCHK ; Set flags on display type JNB C4_03 ; ...text mode, skip MOV CH,28h DIV DL MOV BL,AH MOV BH,0 MOV CL,3 SHL BX,CL MOV CH,AL SHL CH,1 MOV DL,AH MOV DH,AL SHR DH,1 SHR DH,1 CMP Byte ptr DS:49h,6 ; Mode 640 x 200 b/w? JNZ C4_04 ; ...no, skip SHL DL,1 SHL BX,1 JMP short C4_04 C4_03: DIV Byte ptr DS:4Ah ; Divide by columns in screen XCHG AL,AH ; ...as this is text mode MOV DX,AX MOV CL,3 SHL AH,CL MOV CH,AH MOV BL,AL MOV BH,0 SHL BX,CL C4_04: MOV Byte ptr [BP+3],1 ; Return AH=1, light pen read MOV [BP+8],DX ; ...row, column in user DX MOV [BP+4],BX ; ...pixel column in user BX MOV [BP+7],CH ; ...raster line in user CH C4_05: MOV DX,DS:63h ; Get port of active CRT card ADD DX,7 OUT DX,AL ; ...reset the light pen RET CRT_5: MOV AL,[BP+2] ; Set active display page to AL MOV DS:62h,AL ; ...save new active page MOV AH,0 ; ...clear hi order PUSH AX MOV BX,DS:4Ch ; Get size of regen. buffer MUL BX ; ...times number of pages MOV DS:4Eh,AX ; Now AX = CRT offset, save SHR AX,1 ; ...now word offset MOV CX,AX ; ...save a copy MOV AH,0Ch ; CRT index register 0Ch CALL OT6845 ; ...send CH,CL thru CRT reg POP BX CALL MOVCUR ; Save new parameters RET CRT_6: ; Scroll active page up CRT_7: CALL MODCHK ; Scroll active page down JNB SCR_01 JMP SCG_01 ; Graphics scroll SCR_01: CLD ; Strings go upward CMP Byte ptr DS:49h,2 JB SCR_03 ; ...no retrace wait needed CMP Byte ptr DS:49h,3 JA SCR_03 ; ...no retrace wait needed MOV DX,3DAh ; Else 80 x 25, do the kludge SCR_02: IN AL,DX ; Read CGA status register TEST AL,00001000b ; ...vertical retrace? JZ SCR_02 ; ...wait until it is MOV DX,3D8h ; Then go and MOV AL,25h ; ...turn the display OUT DX,AL ; ...off to avoid snow SCR_03: MOV AX,[BP+8] ; Get row,column of upper left PUSH AX CMP Byte ptr [BP+3],7 ; Check for scroll down JZ SCR_04 ; ...yes, skip if so MOV AX,[BP+6] ; Get row,column of lowr right SCR_04: CALL RC2COL ; Get byte offset in CRT buf ADD AX,DS:4Eh ; ...add base for CRT buf MOV SI,AX MOV DI,AX POP DX SUB DX,[BP+6] ; Subtract (row,col) lwr rhgt ADD DX,101h ; ...width of one char MOV BX,DS:4Ah ; Get columns in display SHL BX,1 ; ...bytes in row of display PUSH DS MOV AL,[BP+2] ; Get scroll fill character CALL MAPBYT ; ...calculate offset MOV ES,CX ; CX --> byte in buffer MOV DS,CX CMP Byte ptr [BP+3],6 ; Scroll up? JZ SCR_05 ; ...skip if so NEG AX NEG BX STD ; Else start at top of page SCR_05: MOV CL,[BP+2] ; Get count of lines to scroll OR CL,CL JZ SCR_07 ; ...nothing to do ADD SI,AX SUB DH,[BP+2] SCR_06: MOV CH,0 ; Clear hi order word count MOV CL,DL ; ...load lo order word count PUSH DI PUSH SI REPZ MOVSW ; Do the scroll POP SI POP DI ADD SI,BX ; Move one line in direction ADD DI,BX ; "" "" DEC DH ; One less line to scroll JNZ SCR_06 MOV DH,[BP+2] ; Now get number of rows SCR_07: MOV CH,0 ; Clear hi order word count MOV AH,[BP+5] ; ...get fill attribute MOV AL,' ' ; ...fill character SCR_08: MOV CL,DL ; Get characters to scroll PUSH DI REPZ STOSW ; ...store fill attr/char POP DI ADD DI,BX ; Show row was filled DEC DH JNZ SCR_08 ; ...more rows are left POP DS CALL MODCHK ; Check for monochrome card JZ SCR_09 ; ...skip if so MOV AL,DS:65h ; Get the mode data byte MOV DX,3D8h ; ...load active CRT card port OUT DX,AL ; ...and unblank the screen SCR_09: RET SCG_01: CLD ; Assume GRAFIX scroll up MOV AX,[BP+8] ; (Row,Col) of lower right PUSH AX CMP Byte ptr [BP+3],7 ; Scroll down? JZ SCG_02 ; ...skip if so MOV AX,[BP+6] ; (Row,Col) of upper left SCG_02: CALL GRAMAP ; Convert (Row,Col) -> Chars MOV DI,AX POP DX SUB DX,[BP+6] ; Chars to copy over ADD DX,101h ; ...width of one char SHL DH,1 SHL DH,1 MOV AL,[BP+3] ; Get command type CMP Byte ptr DS:49h,6 ; ...is this 640 x 200? JZ SCG_03 ; ...skip if so SHL DL,1 ; Else bigger characters SHL DI,1 CMP AL,7 ; Is this scroll down? JNZ SCG_03 ; ...skip if not so INC DI SCG_03: CMP AL,7 ; Is this scroll down? JNZ SCG_04 ; ...skip if not so ADD DI,0F0h SCG_04: MOV BL,[BP+2] ; Number of rows to blank SHL BL,1 SHL BL,1 PUSH BX SUB DH,BL ; Subtract from row count MOV AL,50h MUL BL MOV BX,1FB0h CMP Byte ptr [BP+3],6 ; Is this scroll up? JZ SCG_05 ; ...skip if so NEG AX ; Else do it MOV BX,2050h STD ; ...in reverse SCG_05: MOV SI,DI ; End of area ADD SI,AX ; ...start POP AX OR AL,AL MOV CX,[BP+0] MOV DS,CX MOV ES,CX JZ SCG_07 ; No rows to scroll PUSH AX SCG_06: MOV CH,0 ; Zero hi order byte count MOV CL,DL ; ...bytes in row PUSH SI PUSH DI REPZ MOVSB ; Copy one plane POP DI POP SI ADD SI,2000h ; Load other grafix ADD DI,2000h ; ...video plane MOV CL,DL PUSH SI PUSH DI REPZ MOVSB ; Copy other plane POP DI POP SI SUB SI,BX SUB DI,BX DEC DH ; One less row to scroll JNZ SCG_06 ; ...loop if more to do POP AX MOV DH,AL ; Load rows to blank SCG_07: MOV AL,[BP+5] ; Get fill attribute MOV CH,0 SCG_08: MOV CL,DL ; Get bytes per row PUSH DI REPZ STOSB ; Load row with fill attr. POP DI ADD DI,2000h ; Do other grafix video plane MOV CL,DL PUSH DI REPZ STOSB ; Load row with fill attr. POP DI SUB DI,BX DEC DH ; Show one less row to blank JNZ SCG_08 ; ...loop if more to do RET CRT_8: ; Read attribute/character CRT_9: ; Write attribute/character CRT_10: CALL MODCHK ; Write character only JB CG8_01 ; ... graphics operation MOV BL,[BP+5] ; Get the display page MOV BH,0 PUSH BX CALL MPRC2C ; Convert Row,Col,Page -> Col MOV DI,AX ; ...offset in DI POP AX MUL Word ptr DS:4Ch ; Page length X page number ADD DI,AX ; ...current char. position MOV SI,DI ; ...move into si MOV DX,DS:63h ; Display port into DX ADD DX,6 ; ...get status port PUSH DS MOV BX,[BP+0] ; BX --> regen. buffer MOV DS,BX MOV ES,BX MOV AL,[BP+3] ; Get user (AH) func request CMP AL,8 JNZ C9_01 ; ...skip if not read attr C8_01: IN AL,DX ; Read CRT display status TEST AL,00000001b ; ...test for hor. retrace JNZ C8_01 ; Yes, wait for display on CLI ; ...no interrupts now C8_02: IN AL,DX ; Read CRT display status TEST AL,00000001b ; ...test for hor. retrace JZ C8_02 ; ...not yet, wait for it LODSW ; Read character/attribute POP DS MOV [BP+2],AL ; Return character MOV [BP+3],AH ; ..and attribute RET C9_01: MOV BL,[BP+2] ; Get char. to write MOV BH,[BP+4] ; ...attribute MOV CX,[BP+6] ; ...character count CMP AL,0Ah ; Write char. only? JZ CA_01 ; ...skip if so C9_02: IN AL,DX ; Read CRT display status TEST AL,00000001b ; ...test for hor. retrace JNZ C9_02 ; Yes, wait for display on CLI ; ...no interrupts now C9_03: IN AL,DX ; Read CRT display status TEST AL,00000001b ; ...test for hor. retrace JZ C9_03 ; ...not yet, wait for it MOV AX,BX ; Get char/attribute STOSW ; ...write it LOOP C9_02 ; ...loop for char. count POP DS RET CA_01: IN AL,DX ; Read CRT display status TEST AL,00000001b ; ...test for hor. retrace JNZ CA_01 ; ...not yet, wait for it CLI ; ...no interrupts now CA_02: IN AL,DX ; Read CRT display status TEST AL,00000001b ; ...test for hor. retrace JZ CA_02 ; ...not yet, wait for it MOV AL,BL ; Get character STOSB ; ...write it INC DI ; ...skip attribute LOOP CA_01 ; ...loop for char. count POP DS RET CG8_01: CMP Byte ptr [BP+3],8 ; Read graphics char/attr. ? JNZ CG9_01 ; ...no, must be write JMP CGR_01 ; Else read char/attr. CG9_01: MOV AX,DS:50h ; Get cursor position CALL GRAMAP ; ...convert (row,col) -> col MOV DI,AX ; Save in displacement register PUSH DS MOV AL,[BP+2] ; Get character to write MOV AH,0 OR AL,AL ; Is it user character set? JS CG9_02 ; ...skip if so MOV DX,CS ; Else use ROM character set MOV SI,offset GRAFIX ; ...offset GRAFIX into SI JMP short CG9_03 CG9_02: AND AL,7Fh ; Origin to zero XOR BX,BX ; ...then go load MOV DS,BX ; ...user grafix LDS SI,Dword ptr DS:7Ch ; ...vector, offset in SI MOV DX,DS ; ...segment into DX CG9_03: POP DS ; Restore data segment MOV CL,3 ; ...char 8 pixels wide SHL AX,CL ADD SI,AX ; Add regen. buffer base addr. MOV AX,[BP+0] ; ...get regen buffer addr. MOV ES,AX ; ...into ES MOV CX,[BP+6] ; ...load char. count CMP Byte ptr DS:49h,6 ; Is the mode 640 x 200 b/w? PUSH DS MOV DS,DX JZ CG8_02 ; ...skip if so SHL DI,1 MOV AL,[BP+4] ; Get char. attribute AND AX,3 MOV BX,5555h MUL BX MOV DX,AX MOV BL,[BP+4] CG9_04: MOV BH,8 ; Char 8 pixels wide PUSH DI PUSH SI CG9_05: LODSB ; Read the screen PUSH CX PUSH BX XOR BX,BX MOV CX,8 CG9_06: SHR AL,1 ; Shift bits thru byte RCR BX,1 SAR BX,1 LOOP CG9_06 MOV AX,BX ; Result into AX POP BX POP CX AND AX,DX XCHG AH,AL OR BL,BL JNS CG9_07 XOR AX,ES:[DI] CG9_07: MOV ES:[DI],AX ; Write new word XOR DI,2000h TEST DI,2000h ; Is this other plane? JNZ CG9_08 ; ...nope ADD DI,50h ; Else advance character CG9_08: DEC BH ; Show another char written JNZ CG9_05 ; ...more to go POP SI POP DI INC DI INC DI LOOP CG9_04 POP DS RET CG8_02: MOV BL,[BP+4] ; Get display page MOV DX,2000h ; ...size of grafix plane CG8_03: MOV BH,8 ; Pixel count to write PUSH DI PUSH SI CG8_04: LODSB ; Read from one plane OR BL,BL ; ...done both planes? JNS CG8_05 ; ...skip if not XOR AL,ES:[DI] ; Else load attribute CG8_05: MOV ES:[DI],AL ; Write out attribute XOR DI,DX ; ...get other plane TEST DI,DX ; Done both planes? JNZ CG8_06 ; ...skip if not ADD DI,50h ; Else position for now char CG8_06: DEC BH ; Show row of pixels read JNZ CG8_04 ; ...not done all of them POP SI POP DI INC DI LOOP CG8_03 POP DS RET CGR_01: CLD ; Increment upwards MOV AX,DS:50h ; ...get cursor position CALL GRAMAP ; Convert (row,col) -> columns MOV SI,AX ; ...save in SI SUB SP,8 ; Grab 8 bytes temp storage MOV DI,SP ; ...save base in DI CMP Byte ptr DS:49h,6 ; Mode 640 x 200 b/w? MOV AX,[BP+0] ; ...AX --> CRT regen buffer PUSH DS PUSH DI MOV DS,AX JZ CGR_06 ; Mode is 640 x 200 b/w - skip MOV DH,8 ; Eight pixels high/char SHL SI,1 MOV BX,2000h ; Bytes per video plane CGR_02: MOV AX,[SI] ; Read existing word XCHG AH,AL MOV CX,0C000h ; Attributes to scan for MOV DL,0 CGR_03: TEST AX,CX ; Look for attributes CLC JZ CGR_04 ; ...set, skip STC ; Else show not set CGR_04: RCL DL,1 SHR CX,1 SHR CX,1 JNB CGR_03 ; ...more shifts to go MOV SS:[DI],DL INC DI XOR SI,BX ; Do other video plane TEST SI,BX ; ...done both planes? JNZ CGR_05 ; ...no, skip ADD SI,50h ; Else advance pointer CGR_05: DEC DH ; Show another pixel row done JNZ CGR_02 ; ...more rows to do JMP short CGR_08 CGR_06: MOV DH,4 ; Mode 640 x 200 b/w - special CGR_07: MOV AH,[SI] ; Read pixels from one plane MOV SS:[DI],AH ; ...save on stack INC DI ; ...advance MOV AH,[SI+2000h] ; Read pixels from other plane MOV SS:[DI],AH ; Save pixels on stack INC DI ; ...advance ADD SI,50h ; Total pixels in char DEC DH ; ...another row processed JNZ CGR_07 ; ...more to do CGR_08: MOV DX,CS ; Load segment of grafix char MOV DI,offset GRAFIX ; ...and offset MOV ES,DX ; ...save offset in ES MOV DX,SS MOV DS,DX POP SI MOV AL,0 CGR_09: MOV DX,80h ; Number of char. in grafix set CGR_10: PUSH SI PUSH DI MOV CX,8 ; Bytes to compare for char REPZ CMPSB ; ...do compare POP DI POP SI JZ CGR_11 ; Found grafix character INC AL ; ...else show another char ADD DI,8 ; ...advance one row DEC DX ; ...one less char to scan JNZ CGR_10 ; Loop if more char left OR AL,AL ; User grafix character set? JZ CGR_11 ; ...no, not found XOR BX,BX MOV DS,BX LES DI,Dword ptr DS:7Ch ; Else load user grafix char MOV BX,ES OR BX,DI JZ CGR_11 ; ...not found JMP short CGR_09 ; Try using user grafix char CGR_11: MOV [BP+2],AL ; Return char in user AL POP DS ADD SP,8 ; ...return temp storage RET CRT_11: MOV DX,DS:63h ; Set color, get CGA card port ADD DX,5 ; ...color select register MOV AL,DS:66h ; Get CRT palette MOV AH,[BP+5] ; ...new palette ID, user BH OR AH,AH MOV AH,[BP+4] ; ...new palette color, user BL JNZ C_PAL1 ; Palette ID specified, skip AND AL,0E0h AND AH,1Fh ; Null ID = ID 01Fh OR AL,AH ; ...set in color JMP short C_PAL2 C_PAL1: AND AL,0DFh TEST AH,1 JZ C_PAL2 OR AL,20h C_PAL2: MOV DS:66h,AL ; Save new palette OUT DX,AL ; ...tell CGA about it RET CRT_12: MOV AX,[BP+0] ; Write pixel MOV ES,AX MOV DX,[BP+8] ; Load row from user DX MOV CX,[BP+6] ; ... col from user CX CALL LOCDOT ; Find dot offset JNZ WD_01 ; ...valid MOV AL,[BP+2] ; Load user color MOV BL,AL AND AL,1 ROR AL,1 MOV AH,7Fh JMP short WD_02 WD_01: SHL CL,1 MOV AL,[BP+2] MOV BL,AL AND AL,3 ROR AL,1 ROR AL,1 MOV AH,3Fh WD_02: ROR AH,CL SHR AL,CL MOV CL,ES:[SI] ; Read the char with the dot OR BL,BL JNS WD_03 XOR CL,AL ; Exclusive or existing color JMP short WD_04 WD_03: AND CL,AH ; Set new color for dot OR CL,AL WD_04: MOV ES:[SI],CL ; Write out char with the dot RET CRT_13: MOV AX,[BP+0] ; AX --> video regen buffer MOV ES,AX ; ...into ES segment MOV DX,[BP+8] ; Load row from user DX MOV CX,[BP+6] ; ... col from user CX CALL LOCDOT ; Calculate dot offset MOV AL,ES:[SI] ; ...read dot JNZ RD_01 ; ...was there SHL AL,CL ROL AL,1 AND AL,1 JMP short RD_02 RD_01: SHL CL,1 ; Calculate offset in char SHL AL,CL ROL AL,1 ROL AL,1 AND AL,3 RD_02: MOV [BP+2],AL ; Return dot pos in user AL RET CRT_14: MOV BL,DS:62h ; Get active video page (0-7) SHL BL,1 ; ...as word index MOV BH,0 ; ...clear hi order MOV DX,[BX+50h] ; Index into cursor position MOV AL,[BP+2] ; Get char. to write CMP AL,8 ; ...back space? JZ TTY_BS ; ...skip if so CMP AL,LF ; Is it a carriage return JZ TTY_LF ; ...skip if so CMP AL,7 ; Print a bell? JZ BLIP ; ...do beep CMP AL,CR ; Is it a line feed? JZ TTY_CR ; ...skip if so MOV BL,[BP+4] ; Else write at cur pos MOV AH,0Ah MOV CX,1 ; ...one time INT 10h INC DL ; Advance cursor CMP DL,DS:4Ah ; ...check for line overflow JNZ TTYPOS MOV DL,0 ; Overflowed, then fake JMP short TTY_LF ; ...new line TTY_BS: CMP DL,0 ; At start of line? JZ TTYPOS ; ...skip if so DEC DL ; Else back up JMP short TTYPOS ; ...join common code BLIP: MOV BL,2 ; Do a short CALL BEEP ; ...beep RET TTY_CR: MOV DL,0 ; Position to start of line ; JMP short TTYPOS TTYPOS: MOV BL,DS:62h ; Get active video page (0-7) SHL BL,1 ; ...as word index MOV BH,0 ; ...clear hi order MOV [BX+50h],DX ; Remember the cursor position JMP SETCUR ; ...set 6845 cursor hardware TTY_LF: CMP DH,18h ; Done all 24 lines on page? JZ TTY_L1 ; ...yes, scroll INC DH ; Else advance line JNZ TTYPOS TTY_L1: MOV AH,2 ; Position cursor at line start INT 10h CALL MODCHK ; Is this text mode? MOV BH,0 JB TTY_L2 ; Skip if text mode MOV AH,8 INT 10h ; ...else read attribute MOV BH,AH TTY_L2: MOV AH,6 ; Now prepare to MOV AL,1 ; ...scroll XOR CX,CX ; ...the MOV DH,18h ; ...page MOV DL,DS:4Ah ; ...up DEC DL INT 10h RET CRT_15: MOV AL,DS:4Ah ; Get current video state MOV [BP+3],AL ; ...columns MOV AL,DS:49h MOV [BP+2],AL ; ...mode MOV AL,DS:62h MOV [BP+5],AL ; ...page RET MODCHK: PUSH AX ; Set flags acc. to cur. mode MOV AL,DS:49h ; ...get mode CMP AL,7 ; ...EQU if mono JZ MODCH1 CMP AL,4 CMC JNB MODCH1 ; ...carry set on graphix SBB AL,AL STC MODCH1: POP AX RET LOCDOT: MOV AL,50h ; Dots in char. position XOR SI,SI SHR DL,1 ; Two bytes/char. position JNB LOCDO1 ; ...not overflow MOV SI,2000h ; Else on other video plane LOCDO1: MUL DL ; Multiply position by row ADD SI,AX ; ...add in column position MOV DX,CX ; Copy column position MOV CX,302h ; ...regular char size CMP Byte ptr DS:49h,6 ; Mode 640 x 200, b/w? PUSHF JNZ LOCDO2 ; ...skip if not MOV CX,703h ; Else special char. size LOCDO2: AND CH,DL SHR DX,CL ADD SI,DX XCHG CL,CH POPF RET PENXY: CALL PENXY1 ; Read light pen position HI MOV CH,AL ; ...save in CH INC AH CALL PENXY1 ; Read light pen position LO MOV CL,AL ; ...save in CL RET PENXY1: PUSH DX ; Read CRT register offset AL MOV DX,DS:63h ; ...get active CRT port XCHG AL,AH OUT DX,AL ; Send initialization byte INC DL ; ...increment IN AL,DX ; Read pen position byte back POP DX RET MPRC2C: MOV BH,0 ; Convert Row,Col,Page -> Col SHL BX,1 ; ...two bytes/column MOV AX,[BX+50h] ; Get page number in AX ; ...join common code RC2COL: PUSH BX ; Map (AH=row,AL=COL) to COL MOV BL,AL MOV AL,AH MUL Byte ptr DS:4Ah ; Multiply ROW x (Row/Column) MOV BH,0 ADD AX,BX ; ...add in existing COL SHL AX,1 ; ...times 2 cause 2 bytes/col POP BX RET GRAMAP: PUSH BX ; Convert (row,col) -> col MOV BL,AL ; ...save column MOV AL,AH ; ...get row MUL Byte ptr DS:4Ah ; Multiply by columns/row SHL AX,1 SHL AX,1 MOV BH,0 ADD AX,BX ; Add in columns POP BX RET SETCUR: SHR BL,1 ; Sets 6845 cursor position CMP DS:62h,BL ; ...is this page visible? JNZ SEND01 ; No, do nothing in hardware MOVCUR: CALL MPRC2C ; Map row,col,page to col ADD AX,DS:4Eh ; + byte offset, regen reg. SHR AX,1 MOV CX,AX MOV AH,0Eh ; Tell 6845 video controller ; ...to position the cursor OT6845: MOV AL,CH ; Send CH,CL thru CRT reg AH CALL SENDAX ; ...send CH INC AH ; ...increment MOV AL,CL ; ...send CL SENDAX: PUSH DX MOV DX,DS:63h ; Load active video port XCHG AL,AH OUT DX,AL ; Send hi order XCHG AL,AH INC DL OUT DX,AL ; ... lo order POP DX SEND01: RET ENTRY 0F841h ; IBM entry for memory size INT_12: STI ; Kbytes of memory present PUSH DS MOV AX,40h MOV DS,AX MOV AX,DS:13h ; AX = memory size, kilobytes POP DS IRET ENTRY 0F84Dh ; IBM entry for equipment check INT_11: STI ; Equipment present PUSH DS MOV AX,40h MOV DS,AX MOV AX,DS:10h ; AX = equipment byte contents POP DS IRET ENTRY 0F859h ; IBM entry for cassette int. INT_15: STC ; Cassette service (error ret) MOV AH,86h RETF 2 ENTRY 0F85Fh ; IBM non-maskable int. entry INT_2: PUSH AX ; Non-maskable interrupt IN AL,62h TEST AL,11000000b ; Get cause of interrupt JNZ PAR_01 ; ...parity error JMP PAR_07 ; ...math coprocessor (?) PAR_01: PUSH BX ; Parity error bomb PUSH CX PUSH DX PUSH SI PUSH DI PUSH BP PUSH DS PUSH ES MOV AX,40h ; Load data segment MOV DS,AX CALL V_INIT ; ...clear/init screen PUSH DS PUSH CS ; Point DS at ROM POP DS MOV SI,offset BOMB_1 ; SI --> Parity message CALL PRINT ; ...print POP DS ; ...restore DS MOV AX,11h ; Back cursor over ? marks CALL LOCATE ; ...with call MOV AL,0 OUT 0A0h,AL ; ...disable NMI interrupts MOV DX,61h IN AL,DX ; Get machine flags OR AL,00110000b ; ...disable parity int. OUT DX,AL ; Put out new flags AND AL,11001111b ; ...enable parity int. OUT DX,AL ; Put out new flags MOV CL,6 MOV BX,DS:13h ; Get memory size (K bytes) SHL BX,CL INC DX ; ...now paragraphs XOR AX,AX MOV DS,AX PAR_02: MOV CX,10h ; Iterations to check XOR SI,SI PAR_03: MOV AH,[SI] ; Read the byte (dummy) IN AL,DX ; ...and read status TEST AL,11000000b ; ...to see what happened JNZ PAR_04 ; Read caused parity error INC SI ; ...else advance pointer LOOP PAR_03 ; ...and try next byte MOV AX,DS INC AX ; ...next paragraph MOV DS,AX CMP AX,BX JNZ PAR_02 ; More paragraphs to check JMP short PAR_05 ; ...else flakey error PAR_04: MOV [SI],AH ; Save offset in paragraph MOV AX,DS CALL BIGNUM ; Print segment MOV AX,SI CALL DIGIT ; Print offset PAR_05: MOV AX,16h ; Where to position cursor CALL LOCATE ; ...position cursor PUSH DS PUSH CS POP DS MOV SI,offset BOMB_2 ; Continue ? CALL PRINT ; ...ask the user POP DS IN AL,21h ; Get interrupt masks PUSH AX ; ...save them MOV AL,11111100b OUT 21h,AL ; Disable all but keyboard STI ; ...enable interrupt system CALL GETCH ; Get keyboard character PUSH AX ; ...save it CALL OUTCHR ; Print ascii character POP AX ; ...restore CMP AL,'Y' ; User wants to continue JZ PAR_06 ; ...stupid answer CMP AL,'y' ; Look for little case "y" JZ PAR_06 ; ...stupid answer JMP COLD ; Retry on cold reboot PAR_06: CALL BLANK ; Clear display POP AX OUT 21h,AL ; Restore interrupt system state MOV DX,61h ; Dismiss the NMI interrupt IN AL,DX ; ...read in machine flags OR AL,00110000b OUT DX,AL ; Write out, parity disabled AND AL,11001111b ; ...clears parity error OUT DX,AL ; Write out, parity enabled MOV AL,80h OUT 0A0h,AL ; Enable NMI interrupts POP ES POP DS POP BP POP DI POP SI POP DX POP CX POP BX PAR_07: POP AX IRET BOMB_1 db 'Parity error at: ?????',0 BOMB_2 db ' Cont?',0 NUMBER: PUSH AX ; Save number MOV CL,4 SHR AL,CL CALL DIGIT ; Out first digit POP AX CALL DIGIT ; Out second digit RET BIGNUM: PUSH AX ; Unsigned word MOV AL,AH CALL NUMBER POP AX CALL NUMBER RET OUTCHR: PUSH BX PUSH AX MOV AH,0Eh ; Teletype print service MOV BL,7 ; ...normal intensity INT 10h POP AX POP BX RET DIGIT: PUSH AX ; Print hex digit in AL AND AL,0Fh CMP AL,9 JBE D_01 ADD AL,'A'-'9'-1 D_01: ADD AL,'0' ; Make ascii digit CALL OUTCHR ; ...print it POP AX RET MOV AL,CR ; Print carriage return CALL OUTCHR ; ...on screen MOV AL,LF ; Print line feed CALL OUTCHR ; ...on screen RET GETCH: MOV AH,0 ; Read keyboard key INT 16h RET PRINT: LODSB ; Print zero terminated string OR AL,AL JNZ PRINT1 ; ...not terminator in AX RET PRINT1: CALL OUTCHR ; Print character in AX JMP PRINT ; ...back for more BEEP: PUSH AX PUSH CX MOV AL,10110110b ; Timer ic 8253 square waves OUT 43h,AL ; ...channel 2, speaker MOV AX,528h ; Get countdown constant word OUT 42h,AL ; ...send lo order MOV AL,AH ; ...load hi order OUT 42h,AL ; ...send hi order IN AL,61h ; Read ic 8255 machine status PUSH AX OR AL,00000011b OUT 61h,AL ; Turn speaker on XOR CX,CX BEEP_1: LOOP BEEP_1 DEC BL JNZ BEEP_1 POP AX OUT 61h,AL ; Turn speaker off POP CX POP AX RET V_INIT: MOV AH,DS:10h ; Get equipment byte AND AH,00110000b ; ...extract CRT MOV AL,0 ; ...null lo CMP AH,00110000b ; Monochrome? JZ LF9D9 ; ...yes MOV AL,1 ; CGA 40 x 25? CMP AH,00010000b ; ...yes JZ LF9D9 ; CGA 80 x 25? MOV AL,3 ; ...yes LF9D9: MOV AH,0 ; Setup subfunction INT 10h ; ...to video RET BLANK: MOV DX,184Fh ; Lower right corner of scroll XOR CX,CX ; Upper left corner of scroll MOV AX,600h ; Blank entire window MOV BH,7 ; Set regular cursor INT 10h ; Call video service scroll MOV AH,2 ; Set cursor position XOR DX,DX ; ...upper left corner MOV BH,0 ; ...page 0 INT 10h ; ...call video service RET LOCATE: PUSH DX PUSH BX MOV DX,AX ; Get position for cursor MOV AH,2 MOV BH,0 ; ...page 0 INT 10h POP BX POP DX RET CHKSUM: MOV CX,2000h ; Bytes in 2764 eprom CHK_01: MOV AL,0 ; ...zero checksum ADDBYT: ADD AL,[BX] ; Add byte to checksum INC BX ; ...BX --> next byte LOOP ADDBYT ; ...loop until done OR AL,AL ; Set condition codes RET ; ...and return MEMTST: MOV BX,0400h ; Load bytes to test MOV AL,55h ; PAT_1: XOR DI,DI ; Pattern #1, 55h bytes MOV CX,BX REPZ STOSB ; Fill memory, pattern #1 XOR DI,DI MOV CX,BX REPZ SCASB ; Scan memory for NOT pattern #1 JCXZ PAT_2 STC ; ...flunked RET PAT_2: XOR DI,DI ; Pattern #2 - 0AAh bytes MOV CX,BX NOT AL REPZ STOSB ; Fill memory, pattern #2 XOR DI,DI MOV CX,BX REPZ SCASB ; Scan memory for NOT pattern #2 JCXZ PAT_3 STC ; ...flunked RET PAT_3: XOR DI,DI ; Pattern #3 - 01h bytes MOV CX,BX MOV AL,1 REPZ STOSB ; Fill memory, pattern #3 XOR DI,DI MOV CX,BX REPZ SCASB ; Scan memory for NOT pattern #3 JCXZ PAT_4 STC ; ...flunked RET PAT_4: XOR DI,DI ; Pattern #4 - 0h bytes MOV CX,BX DEC AL REPZ STOSB ; Fill memory, pattern #4 XOR DI,DI MOV CX,BX REPZ SCASB ; Scan memory for NOT pattern #4 JCXZ LFA59 STC ; ...flunked RET LFA59: MOV AX,ES ADD AX,40h ; Add 40h to segment number MOV ES,AX RET ; ...passed ENTRY 0FA6Eh ; IBM graphics char set entry GRAFIX db 000h,000h,000h,000h ; Graphics character set db 000h,000h,000h,000h db 07Eh,081h,0A5h,081h db 0BDh,099h,081h,07Eh db 07Eh,0FFh,0DBh,0FFh db 0C3h,0E7h,0FFh,07Eh db 06Ch,0FEh,0FEh,0FEh db 07Ch,038h,010h,000h db 010h,038h,07Ch,0FEh db 07Ch,038h,010h,000h db 038h,07Ch,038h,0FEh db 0FEh,07Ch,038h,07Ch db 010h,010h,038h,07Ch db 0FEh,07Ch,038h,07Ch db 000h,000h,018h,03Ch db 03Ch,018h,000h,000h db 0FFh,0FFh,0E7h,0C3h db 0C3h,0E7h,0FFh,0FFh db 000h,03Ch,066h,042h db 042h,066h,03Ch,000h db 0FFh,0C3h,099h,0BDh db 0BDh,099h,0C3h,0FFh db 00Fh,007h,00Fh,07Dh db 0CCh,0CCh,0CCh,078h db 03Ch,066h,066h,066h db 03Ch,018h,07Eh,018h db 03Fh,033h,03Fh,030h db 030h,070h,0F0h,0E0h db 07Fh,063h,07Fh,063h db 063h,067h,0E6h,0C0h db 099h,05Ah,03Ch,0E7h db 0E7h,03Ch,05Ah,099h db 080h,0E0h,0F8h,0FEh db 0F8h,0E0h,080h,000h db 002h,00Eh,03Eh,0FEh db 03Eh,00Eh,002h,000h db 018h,03Ch,07Eh,018h db 018h,07Eh,03Ch,018h db 066h,066h,066h,066h db 066h,000h,066h,000h db 07Fh,0DBh,0DBh,07Bh db 01Bh,01Bh,01Bh,000h db 03Eh,063h,038h,06Ch db 06Ch,038h,0CCh,078h db 000h,000h,000h,000h db 07Eh,07Eh,07Eh,000h db 018h,03Ch,07Eh,018h db 07Eh,03Ch,018h,0FFh db 018h,03Ch,07Eh,018h db 018h,018h,018h,000h db 018h,018h,018h,018h db 07Eh,03Ch,018h,000h db 000h,018h,00Ch,0FEh db 00Ch,018h,000h,000h db 000h,030h,060h,0FEh db 060h,030h,000h,000h db 000h,000h,0C0h,0C0h db 0C0h,0FEh,000h,000h db 000h,024h,066h,0FFh db 066h,024h,000h,000h db 000h,018h,03Ch,07Eh db 0FFh,0FFh,000h,000h db 000h,0FFh,0FFh,07Eh db 03Ch,018h,000h,000h db 000h,000h,000h,000h db 000h,000h,000h,000h db 030h,078h,078h,030h db 030h,000h,030h,000h db 06Ch,06Ch,06Ch,000h db 000h,000h,000h,000h db 06Ch,06Ch,0FEh,06Ch db 0FEh,06Ch,06Ch,000h db 030h,07Ch,0C0h,078h db 00Ch,0F8h,030h,000h db 000h,0C6h,0CCh,018h db 030h,066h,0C6h,000h db 038h,06Ch,038h,076h db 0DCh,0CCh,076h,000h db 060h,060h,0C0h,000h db 000h,000h,000h,000h db 018h,030h,060h,060h db 060h,030h,018h,000h db 060h,030h,018h,018h db 018h,030h,060h,000h db 000h,066h,03Ch,0FFh db 03Ch,066h,000h,000h db 000h,030h,030h,0FCh db 030h,030h,000h,000h db 000h,000h,000h,000h db 000h,030h,030h,060h db 000h,000h,000h,0FCh db 000h,000h,000h,000h db 000h,000h,000h,000h db 000h,030h,030h,000h db 006h,00Ch,018h,030h db 060h,0C0h,080h,000h db 07Ch,0C6h,0CEh,0DEh db 0F6h,0E6h,07Ch,000h db 030h,070h,030h,030h db 030h,030h,0FCh,000h db 078h,0CCh,00Ch,038h db 060h,0CCh,0FCh,000h db 078h,0CCh,00Ch,038h db 00Ch,0CCh,078h,000h db 01Ch,03Ch,06Ch,0CCh db 0FEh,00Ch,01Eh,000h db 0FCh,0C0h,0F8h,00Ch db 00Ch,0CCh,078h,000h db 038h,060h,0C0h,0F8h db 0CCh,0CCh,078h,000h db 0FCh,0CCh,00Ch,018h db 030h,030h,030h,000h db 078h,0CCh,0CCh,078h db 0CCh,0CCh,078h,000h db 078h,0CCh,0CCh,07Ch db 00Ch,018h,070h,000h db 000h,030h,030h,000h db 000h,030h,030h,000h db 000h,030h,030h,000h db 000h,030h,030h,060h db 018h,030h,060h,0C0h db 060h,030h,018h,000h db 000h,000h,0FCh,000h db 000h,0FCh,000h,000h db 060h,030h,018h,00Ch db 018h,030h,060h,000h db 078h,0CCh,00Ch,018h db 030h,000h,030h,000h db 07Ch,0C6h,0DEh,0DEh db 0DEh,0C0h,078h,000h db 030h,078h,0CCh,0CCh db 0FCh,0CCh,0CCh,000h db 0FCh,066h,066h,07Ch db 066h,066h,0FCh,000h db 03Ch,066h,0C0h,0C0h db 0C0h,066h,03Ch,000h db 0F8h,06Ch,066h,066h db 066h,06Ch,0F8h,000h db 0FEh,062h,068h,078h db 068h,062h,0FEh,000h db 0FEh,062h,068h,078h db 068h,060h,0F0h,000h db 03Ch,066h,0C0h,0C0h db 0CEh,066h,03Eh,000h db 0CCh,0CCh,0CCh,0FCh db 0CCh,0CCh,0CCh,000h db 078h,030h,030h,030h db 030h,030h,078h,000h db 01Eh,00Ch,00Ch,00Ch db 0CCh,0CCh,078h,000h db 0E6h,066h,06Ch,078h db 06Ch,066h,0E6h,000h db 0F0h,060h,060h,060h db 062h,066h,0FEh,000h db 0C6h,0EEh,0FEh,0FEh db 0D6h,0C6h,0C6h,000h db 0C6h,0E6h,0F6h,0DEh db 0CEh,0C6h,0C6h,000h db 038h,06Ch,0C6h,0C6h db 0C6h,06Ch,038h,000h db 0FCh,066h,066h,07Ch db 060h,060h,0F0h,000h db 078h,0CCh,0CCh,0CCh db 0DCh,078h,01Ch,000h db 0FCh,066h,066h,07Ch db 06Ch,066h,0E6h,000h db 078h,0CCh,0E0h,070h db 01Ch,0CCh,078h,000h db 0FCh,0B4h,030h,030h db 030h,030h,078h,000h db 0CCh,0CCh,0CCh,0CCh db 0CCh,0CCh,0FCh,000h db 0CCh,0CCh,0CCh,0CCh db 0CCH,078h,030h,000h db 0C6h,0C6h,0C6h,0D6h db 0FEh,0EEh,0C6h,000h db 0C6h,0C6h,06Ch,038h db 038h,06Ch,0C6h,000h db 0CCh,0CCh,0CCh,078h db 030h,030h,078h,000h db 0FEh,0C6h,08Ch,018h db 032h,066h,0FEh,000h db 078h,060h,060h,060h db 060h,060h,078h,000h db 0C0h,060h,030h,018h db 00Ch,006h,002h,000h db 078h,018h,018h,018h db 018h,018h,078h,000h db 010h,038h,06Ch,0C6h db 000h,000h,000h,000h db 000h,000h,000h,000h db 000h,000h,000h,0FFh db 030h,030h,018h,000h db 000h,000h,000h,000h db 000h,000h,078h,00Ch db 07Ch,0CCh,076h,000h db 0E0h,060h,060h,07Ch db 066h,066h,0DCh,000h db 000h,000h,078h,0CCh db 0C0h,0CCh,078h,000h db 01Ch,00Ch,00Ch,07Ch db 0CCh,0CCh,076h,000h db 000h,000h,078h,0CCh db 0FCh,0C0h,078h,000h db 038h,06Ch,060h,0F0h db 060h,060h,0F0h,000h db 000h,000h,076h,0CCh db 0CCh,07Ch,00Ch,0F8h db 0E0h,060h,06Ch,076h db 066h,066h,0E6h,000h db 030h,000h,070h,030h db 030h,030h,078h,000h db 00Ch,000h,00Ch,00Ch db 00Ch,0CCh,0CCh,078h db 0E0h,060h,066h,06Ch db 078h,06Ch,0E6h,000h db 070h,030h,030h,030h db 030h,030h,078h,000h db 000h,000h,0CCh,0FEh db 0FEh,0D6h,0C6h,000h db 000h,000h,0F8h,0CCh db 0CCh,0CCh,0CCh,000h db 000h,000h,078h,0CCh db 0CCh,0CCh,078h,000h db 000h,000h,0DCh,066h db 066h,07Ch,060h,0F0h db 000h,000h,076h,0CCh db 0CCh,07Ch,00Ch,01Eh db 000h,000h,0DCh,076h db 066h,060h,0F0h,000h db 000h,000h,07Ch,0C0h db 078h,00Ch,0F8h,000h db 010h,030h,07Ch,030h db 030h,034h,018h,000h db 000h,000h,0CCh,0CCh db 0CCh,0CCh,076h,000h db 000h,000h,0CCh,0CCh db 0CCh,078h,030h,000h db 000h,000h,0C6h,0D6h db 0FEh,0FEh,06Ch,000h db 000h,000h,0C6h,06Ch db 038h,06Ch,0C6h,000h db 000h,000h,0CCh,0CCh db 0CCh,07Ch,00Ch,0F8h db 000h,000h,0FCh,098h db 030h,064h,0FCh,000h db 01Ch,030h,030h,0E0h db 030h,030h,01Ch,000h db 018h,018h,018h,000h db 018h,018h,018h,000h db 0E0h,030h,030h,01Ch db 030h,030h,0E0h,000h db 076h,0DCh,000h,000h db 000h,000h,000h,000h db 000h,010h,038h,06Ch db 0C6h,0C6h,0FEh,000h ENTRY 0FE6Eh ; IBM entry, time_of_day clock INT_1A: STI ; User time_of_day bios service PUSH DS PUSH AX MOV AX,40h MOV DS,AX POP AX ; Get request type CLI ; ...freeze clock OR AH,AH JZ TD_01 ; Read time, AH=0 DEC AH JNZ TD_02 ; ...invalid request MOV DS:6Ch,DX ; Set time, AH=1 MOV DS:6Eh,CX ; ...set time hi MOV Byte ptr DS:70h,0 ; ...not a new day JMP short TD_02 TD_01: MOV CX,DS:6Eh ; Read lo order time MOV DX,DS:6Ch ; ... hi order time CALL TD_03 ; Read resets overflow TD_02: STI ; Unfreeze clock POP DS IRET TD_03: MOV AL,DS:70h ; Zero the overflow and return XOR DS:70h,AL ; ...previous status in flags RET ENTRY 0FEA5h ; IBM entry, hardware clock INT_8: STI ; Routine services clock tick PUSH DS PUSH DX PUSH AX MOV AX,40h MOV DS,AX DEC Byte ptr DS:40h ; Decrement motor count JNZ TI_01 ; ...not time to shut off AND Byte ptr DS:3Fh,11110000b ; Else show motor off MOV AL,0Ch ; ...send motor off MOV DX,3F2h ; ...to the floppy OUT DX,AL ; ...disk controller TI_01: INC Word ptr DS:6Ch ; Bump lo order time of day JNZ TI_02 ; ...no carry INC Word ptr DS:6Eh ; Bump hi order time of day TI_02: CMP Word ptr DS:6Eh,18h ; Is it midnight yet? JNZ TI_03 ; ...no CMP Word ptr DS:6Ch,0B0h ; Possibly, check lo order JNZ TI_03 ; ...not midnight MOV Word ptr DS:6Eh,0 ; Midnight, reset hi order MOV Word ptr DS:6Ch,0 ; ...lo order ticks MOV Byte ptr DS:70h,1 ; Show new day since last read TI_03: INT 1Ch ; Execute user clock service MOV AL,20h ; ...send end_of_interrupt OUT 20h,AL ; ...to 8259 interrupt chip POP AX POP DX POP DS IRET ENTRY 0FEF3h ; IBM entry, time_of_day clock VECTORS dw int_8 ; Timer tick dw int_9 ; Key attention dw IGNORE ; Reserved dw IGNORE ; Reserved for COM2 serial i/o dw IGNORE ; Reserved for COM1 serial i/o dw IGNORE ; Reserved for hard disk attn. dw int_e ; Floppy disk attention dw IGNORE ; Reserved for parallel printer dw int_10 ; Video bios services dw int_11 ; Equipment present dw int_12 ; Memories present dw int_13 ; Disk bios services dw int_14 ; Serial com. services dw int_15 ; Cassette bios services dw int_16 ; Keyboard bios services dw int_17 ; Parallel printer services dw IGNORE ; rom Basic (setup later) dw int_19 ; Bootstrap dw int_1a ; Timer bios services dw DUMMY ; Keyboard break user service dw DUMMY ; System tick user service dw int_1d ; Video parameter table dw int_1e ; Disk parameter table dw ? ; Graphic charactr table ptr ENTRY 0FF23h ; IBM entry, nonsense interrupt IGNORE: PUSH DS ; Unexpected interrupts go here PUSH DX PUSH AX MOV AX,40h MOV DS,AX MOV AL,0Bh ; What IRQ caused this? OUT 20h,AL NOP IN AL,20h ; ...(read IRQ level) MOV AH,AL OR AL,AL JNZ DU_1 MOV AL,0FFh ; Not hardware, say 0FFh IRQ JMP short DU_2 DU_1: IN AL,21h ; Clear the IRQ OR AL,AH OUT 21h,AL MOV AL,20h ; Send end_of_interrupt code OUT 20h,AL ; ...to 8259 interrupt chip DU_2: MOV DS:6Bh,AH ; Save last nonsense interrupt POP AX POP DX POP DS IRET ENTRY 0FF53h ; IBM entry, dummy interrupts ;INT_1B: ; Keyboard break user service ;INT_1C: ; Clock tick user service DUMMY: IRET ENTRY 0FF54h ; IBM entry, print screen INT_5: STI ; Print screen service PUSH DS PUSH AX PUSH BX PUSH CX PUSH DX MOV AX,40h MOV DS,AX CMP Byte ptr DS:100h,1 ; Print screen in progress? JZ PS_5 ; ...yes, ignore MOV Byte ptr DS:100h,1 ; Flag print screen in progress CALL P_CRLF ; ...begin new line MOV AH,0Fh INT 10h ; Get current video state PUSH AX ; ...save it MOV AH,3 INT 10h ; Read cursor position POP AX ; ...retrieve video state PUSH DX ; ...save cursor position MOV CH,19h ; Do 25 rows MOV CL,AH ; ...columns in current mode XOR DX,DX ; Start printing from (0,0) PS_1: MOV AH,2 ; Set cursor to position INT 10h MOV AH,8 ; ...and read character INT 10h OR AL,AL ; Nulls are special case JNZ PS_2 MOV AL,' ' ; ...convert to spaces PS_2: PUSH DX XOR DX,DX MOV AH,DL ; Function=Print character INT 17h POP DX TEST AH,00100101b ; Successful print JZ PS_3 MOV Byte ptr DS:100h,0FFh ; No, error in Print Screen JMP short PS_4 PS_3: INC DL ; Increment column count CMP CL,DL JNZ PS_1 ; ...in range, continue MOV DL,0 CALL P_CRLF ; Else print new line INC DH ; ...add another row CMP DH,CH ; Done all 25 rows? JNZ PS_1 ; ...no, continue MOV Byte ptr DS:100h,0 ; Show done Print Screen OK PS_4: POP DX ; Get saved cursor position MOV AH,2 INT 10h ; ...restore it PS_5: POP DX POP CX POP BX POP AX POP DS IRET ENTRY 0FFCBh ; IBM entry, display CR, LF P_CRLF: PUSH DX ; Print CR, LF, on line printer XOR DX,DX MOV AH,DL ; Function=print MOV AL,LF ; LF INT 17h MOV AH,0 MOV AL,CR ; CR INT 17h POP DX RET ;****************************************************************************** ENTRY 0FFF0h ; Hardware power reset entry * PUBLIC POWER ; ...ic "8088" or "V20" * POWER: JMPF 0F000h,COLD ; ...begins here on power up * ;****************************************************************************** ENTRY 0FFF5h ; Release date, Yankee style db "08/23/87" ; ...MM/DD/YY (not logical) ENTRY 0FFFEh db 0FEh ; Computer type (XT) ; db ? ; Checksum byte code ENDS ; END