!************************************************************************** !* !* Network driver interface for netboot bootrom !* !* Module: dosinit.S !* Purpose: Initialize the DOS simulator, setup all interupt vectors etc. !* Entries: initdos, cleandos, call21, unused !* !************************************************************************** !* !* Copyright (C) 1995-2003 Gero Kuhlmann !* !* This program is free software; you can redistribute it and/or modify !* it under the terms of the GNU General Public License as published by !* the Free Software Foundation; either version 2 of the License, or !* any later version. !* !* This program is distributed in the hope that it will be useful, !* but WITHOUT ANY WARRANTY; without even the implied warranty of !* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !* GNU General Public License for more details. !* !* You should have received a copy of the GNU General Public License !* along with this program; if not, write to the Free Software !* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. !* !* $Id: dosinit.S,v 1.6 2003/01/25 23:29:41 gkminix Exp $ !* ! !************************************************************************** ! ! Include assembler macros: ! #include #include #include "dospriv.inc" ! !************************************************************************** ! ! Definitions for the Virtual DMA Specification ! BIOSSEG equ $0040 ! address of VDS status flag in VDSFLAG equ $007B ! BIOS data area ! !************************************************************************** ! ! Define macros to produce jump table entries. ! macro tab extrn dos?1 ! define label as near external .byte $?1 ! save function number .word dos?1 ! save pointer to label mend TABSIZE equ 3 ! number of bytes per table entry ! !************************************************************************** ! ! Interrupt handler table. ! .data inttab: .word $20 * 4, int20 ! terminate program .word $21 * 4, int21 ! dos function scheduler .word $27 * 4, int27 ! terminate and stay resident .word $29 * 4, int29 ! print character INTNUM equ 4 ! number of interrupts in table FRSTINT equ $20 ! first interrupt used by DOS LSTINT equ $2F ! last interrupt used by DOS VECTNUM equ (LSTINT - FRSTINT + 1) ! number of DOS vectors ! !************************************************************************** ! ! The DOS simulator needs its own stack when int$21 is called. This stack ! is placed in the BSS segment. Note that the real DOS uses three different ! stacks. This will not be completely simulated. The stack for function ! $59 is not necessary, so we only simulate two different stacks. The ! stack signature is contained in the registers structure, so this struc- ! ture has to come before the stack itself. ! .bss ! start BSS segment extrn curr_psp ! segment of current psp extrn first_mcb ! segment of first MCB in list STACKSIZE equ 512 ! pretty small, so be careful. .comm dos1_regs,old_reg_size + STACKSIZE .comm dos2_regs,old_reg_size + STACKSIZE .lcomm temp_ax,2 ! temporary storage for AX .lcomm temp_bx,2 ! temporary storage for BX .comm alt_handler,2 ! alternative DOS handler .comm dos_active,1 ! DOS active counter ! !************************************************************************** ! ! Start code segment. ! .text public initdos ! define entry points public cleandos public call21 public unused ! External variables in code space: extrn datseg extrn txtseg ! General library functions: extrn prnchar ! !************************************************************************** ! ! Initialize the mini-DOS simulator module. ! Input: DX - first segment usable for DOS memory ! Output: None ! Registers changed: (E)AX, BX, CX, DX, DI, ES ! initdos: cld ! Initialize DOS memory by setting up the first two memory control blocks. The ! first block has a size of one paragraph and contains all zeros. It can be ! used as an empty environment for calling the DOS programs. push ds mov [first_mcb],dx ! save pointer to first MCB mov ds,dx mov byte ptr [mcb_id],#MCB_MEM_ID mov word ptr [mcb_owner],#$FFFF mov word ptr [mcb_size],#1 add dx,#2 ! the memory block has already been seg cs ! cleared by ROM loader mov ax,[txtseg] ! compute size of free DOS memory dec ax sub ax,dx dec ax mov ds,dx mov byte ptr [mcb_id],#MCB_END_ID mov word ptr [mcb_owner],#0 ! set MCB of first free memory block mov word ptr [mcb_size],ax pop ds ! Install interrupt handlers. We dont have to save the old vectors as ! that will be done by the general interrupt handling routines. init1: xor ax,ax mov es,ax mov di,#FRSTINT * 4 mov cx,#VECTNUM cli #ifdef IS386 mov ax,cs shl eax,#16 mov ax,#unused rep stosd ! save pointer to 'unused' routine #else init2: mov ax,#unused ! save pointer to 'unused' routine stosw mov ax,cs stosw loop init2 #endif mov bx,#inttab ! pointer to interrupt table mov cx,#INTNUM init3: mov di,[bx + 0] ! get pointer to interrupt vector mov ax,[bx + 2] stosw ! save offset, segment is already stored add bx,#4 ! continue with next interrupt loop init3 sti ! Clear VDS (Virtual DMA Specification) status byte in BIOS data area so ! that a bus master network driver doesnt try to accidentally use a non- ! existent VDS service. push ds mov ax,#BIOSSEG mov ds,ax mov byte ptr [VDSFLAG],#0 pop ds ret ! !************************************************************************** ! ! Clean all DOS interrupts from interrupt vector buffer ! Input: SS:BP - buffer of interrupt vectors ! Output: None ! Registers changed: (E)AX, CX, SI, DI ! cleandos: cld push es mov cx,ss mov es,cx lea di,[bp + FRSTINT * 4] #ifdef IS386 mov cx,#VECTNUM ! simply clear all interrupt vectors xor eax,eax ! in the interrupt vector buffer rep stosd #else mov cx,#VECTNUM * 2 ! simply clear all interrupt vectors xor ax,ax ! in the interrupt vector buffer rep stosw #endif pop es ret ! !************************************************************************** ! ! Jump table for main dispatcher interrupt. Put it into code segment ! (instead const segment) to save space in lower ram area. ! jmptab: ! functions for basic I/O: tab (01) tab (02) tab (03) tab (04) tab (05) tab (06) tab (07) tab (08) tab (09) tab (0A) tab (0B) tab (0C) ! functions for system management: tab (0D) tab (0E) tab (19) tab (1A) tab (25) tab (2E) tab (2F) tab (30) tab (34) tab (35) tab (37) tab (54) ! functions for time operations: #ifdef NEEDTIME tab (2A) tab (2B) tab (2C) tab (2D) #endif ! functions for memory and process management: tab (00) tab (31) tab (48) tab (49) tab (4A) tab (4C) tab (4D) tab (58) ! functions for file handling: tab (3C) tab (3D) tab (3E) tab (3F) tab (40) tab (44) .byte $FF ! end of table ! Those functions which were only implemented before DOS2.0 return ! a different error code than those which were introduced with ! DOS2.0 and later. The first "later" function is 30h: DOS20FN equ $30 ! !************************************************************************** ! ! Routine for main dispatcher interrupt 21h. This involves changing to a ! new stack, and then searching for the required function in the function ! table. The function handler has to get the callers data segment from ! old_ds if necessary. ! Input: AH - DOS function number ! others depending on function number ! Output: depending on function number ! Registers changed: depending on function number ! int21: ! First check for functions which dont use an internal stack. int21s: sti push ds seg cs mov ds,[datseg] ! switch to new data segment cmp ah,#$33 ! function 33h is not implemented je nostkr cmp ah,#$64 ! function 64h is not implemented je nostkr cmp ah,#$59 jne isno59 mov ax,#$00FF ! function 59h is not implemented, mov bx,#$0D05 ! so just return some dummy values mov ch,#$01 jmp nostkr isno59: cmp ah,#$50 jne isno50 mov [curr_psp],bx ! implement function $50 jmp nostkr isno50: cmp ah,#$51 je isf51 ! function $51 is the same as $62 cmp ah,#$62 jne int21r isf51: mov bx,[curr_psp] ! implement function $51 and $62 nostkr: pop ds iret ! For all other functions set new stack and then check the function table ! for the function handler. Select the correct stack for the function ! used. If changing anything in this code remember to also change the ! basicio functions! Also note that interrupts have to be disabled while ! using the temporary register variables. int21r: cli mov [temp_ax],ax ! temporarily save callers AX register mov [temp_bx],bx ! temporarily save callers BX register mov bx,bp mov bp,#dos1_regs or ah,ah jz int21v cmp ah,#$0C ! select correct stack jbe int21u int21v: mov bp,#dos2_regs int21u: seg ds pop old_ds[bp] ! save callers DS register seg ds mov old_bp[bp],bx ! save callers BP register seg ds ! save callers stack pointer mov word ptr old_stack + 0[bp],sp seg ds mov word ptr old_stack + 2[bp],ss mov ax,ds lea bx,[bp + old_reg_size + STACKSIZE - 2] mov ss,ax ! set new stack mov sp,bx ! Search for the function in the function table. mov bx,#jmptab mov ax,[temp_ax] int21l: seg cs mov al,[bx] cmp al,#$0FF ! check for end of table je notfnd cmp al,ah ! found function value? je int21f add bx,#TABSIZE ! not found, loop to next jmp int21l ! table entry ! Found table entry, so prepare registers and jump to the routine. This ! is done by putting its address onto the stack, and then executing a ! near return instruction. ! The network driver has the chance of using its own DOS handler by ! setting 'alt_handler' to point to this routine. If the drivers routine ! is willing to handle the DOS function, it has to remove the return ! address and the address of the DOS simulator function from the stack. ! It can then simply return from its routine and will wind up at 'int21t'. int21f: inc byte ptr [dos_active] ! increment active counter #ifdef IS386 push fs #endif #ifdef IS186 push #int21t ! push return address onto stack #else mov ax,#int21t ! push return address onto stack push ax #endif seg cs push word ptr [bx+1] ! push jump address onto stack mov bx,[temp_bx] mov ax,[temp_ax] ! restore all registers except DS sti ! and BP test word ptr [alt_handler],#$FFFF jz int21g call near ptr [alt_handler] int21g: ret ! jump to handler routine ! Return to the caller by restoring the stack without changing any ! registers and flags except carry. int21t: #ifdef IS386 pop fs #endif pushf cli dec byte ptr [dos_active] ! decrement active counter mov [temp_bx],bx int21e: mov [temp_ax],ax pop ax ! get flags seg ds mov bx,word ptr old_stack + 0[bp] seg ds mov ss,word ptr old_stack + 2[bp] mov sp,bx seg ss ! this is the place where the INT mov byte ptr [bx+4],al ! operation did put the flags mov ax,[temp_ax] mov bx,[temp_bx] seg ds push old_bp[bp] seg ds mov ds,old_ds[bp] ! restore callers registers pop bp unused: iret ! We couldnt find the requested function in the function table, so return ! with an error code in AX and the carry flag set. However, if it is a ! FCB related function, return with the proper error code for that. Note ! that interrupts are disabled when entering this code. notfnd: mov al,#$FF ! return with proper error code cmp ah,#DOS20FN ! check if its an old function jb notfn1 mov ax,#ERR_NOFN ! return error code notfn1: stc ! set carry flag to indicate error pushf jmp int21e ! !************************************************************************** ! ! INT20h handler: terminate current program. ! Input: none ! Output: routine does not return ! int20: xor ah,ah jmp near int21s ! just call DOS function 00h ! !************************************************************************** ! ! INT27h handler: terminate current program and stay resident. ! Input: DX - number of bytes in program memory block ! Output: routine does not return ! int27: add dx,#$000F rcr dx,#1 ! round byte count to nearest shift (shr,dx,3) ! paragraph mov ah,#$31 ! just call DOS function 31h jmp near int21s ! !************************************************************************** ! ! INT29h handler: print character onto screen ! Input: AL - character to print ! Output: none ! Registers changed: none ! int29: push ax call prnchar ! just print using the BIOS pop ax iret ! !************************************************************************** ! ! Another way for a program to call the function dispatcher is to call ! address psp:0005. This will jump to the following address. ! call21: push bp mov bp,sp push ax mov ax,[bp + 6] ! get return address to caller mov [bp + 2],ax ! and put it into place pushf pop [bp + 6] ! replace the old return address pop ax ! with the flags. this makes the pop bp ! call look like an INT. jmp near int21s ! !************************************************************************** ! end