page ,132 ;/******************************************************************* ;* * ;* Loughborough Sound Images Ltd * ;* The Technology Centre * ;* Epinal Way * ;* Loughborough * ;* Leics Tel : (0509) 231843 * ;* LE11 0QE FAX : (0509) 262433 * ;* * ;* Software Copyright 1992 * ;* * ;* Date : Monday 22nd March 1993 * ;* Project : QPC/C40 Apps Library * ;* Modules : * ;* * ;* History : * ;* Date Ver Change * ;* 22/3/93 1.00 create preliminary testing * ;* 12/5/93 2.00 create (taken from outline) * ;* 19/5/93 2.01 speed enhancements * ;* * ;*******************************************************************/ ; ; ; This file contains the Assembly Language source for a collection ; of procedures which provide easy access to the link interface adaptor ; in the LSI QPC/C40B PC plug-in card. The procedures can be called from ; an assembly language program or from a high level language program, ; provided that it follows the normal convention of passing the parameters ; on the stack. If the procedure has to return a value, it is left in ; the AX register at the end of the routine (if >16 bits the MSB's are ; in the DX reg). ; ; The following routines are available from this assembler library. ; public _LIA_FWrite_Block, _LIA_FRead_Block public _LIA_Write_Block, _LIA_Read_Block public _LIA_Write, _LIA_Read ; ; General Equates here. ; i_LIA_Hi_Lo_Diff is the difference to be added/ subtracted during ; subsequent accesses to the LIA. We store this in S. Really SI is ; used here as an 'overflow' general purpose register! ; i_LIA_Hi_Lo_Diff equ 2 ; ; We need to access the LIA address that has been set up in Select_Board ; so we declare it as global. ; extrn _Global_LIA_Address:WORD ; ; We really want to have an easy access method to all of our parameters. ; Listed below are the equates to allow this. ; ; Single word access functions ui_Module_No equ [bp+6] ul_Data_Value_Low equ [bp+8] ul_Data_Value_High equ [bp+10] ulp_Data_Value_Offset equ [bp+8] ulp_Data_Value_Segment equ [bp+10] ; ; Block access functions ; ; void far LIA_Write_Block( UINT ui_Module_No, UINT, ui_Number_to_Transfer, ULONG *ul_Data_Block ) ui_Module_No equ [bp+6] ui_Number_to_Transfer equ [bp+8] ul_Data_Block_Offset equ [bp+10] ul_Data_Block_Segment equ [bp+12] ui_Timeout_Period equ [bp+14] ui_Timeout_Period_BHunds equ byte ptr [bp+14] ui_Timeout_Period_BSecs equ byte ptr [bp+15] ; ; When we are moving data out we use these two equates to ease the access ; from ints to ULONGs. ; Ulong_Int_0 equ word ptr [bx+0] Ulong_Int_1 equ word ptr [bx+2] ; ; We have a macro to read and write one word of data to/ from the LIA. ; These allow us to replicate the code to open out the loop and hence ; speed up the execution. It also aids readability onsiderably. ; LIA_Write_One_Word MACRO i_Base_Offset mov ax, word ptr [bx+(0 + i_Base_Offset)] mov dx, di out dx, ax mov ax, word ptr [bx+(2 + i_Base_Offset)] mov dx, si out dx, ax ENDM ; LIA_Read_One_Word MACRO i_Base_Offset mov dx, di in ax, dx mov word ptr [bx + (0 + i_Base_Offset)], ax mov dx, si in ax, dx mov word ptr [bx + (2 + i_Base_Offset)], ax ENDM ; ; Code starts here ; .MODEL large DGROUP GROUP _DATA _DATA SEGMENT WORD PUBLIC 'DATA' ASSUME DS:DGROUP ASSUME ES:DGROUP ; ; This is the lookup table that is used to determine the offset from ; the link interface base for the link that we need to talk to. ; Unfortunately they do not map directly and the mappings are : ; ; Module_A --> offset +4 from base ; Module_B --> offset +12 from base ; Module_C --> offset +0 from base ; Module_D --> offset +8 from base ; LIA_Module_Offset_Table dw 4 dw 12 dw 0 dw 8 ; ; For the FRead and FWrite routines we need to decided if we can ; transmit or recieve data yet. We therfore need these masks to ; determine which bit to mask out. Stored in the same order as ; above. Once we have determined which to use then we move it to ; a known location. ; LIA_XMit_Data_Empty_Masks dw 8h dw 800h dw 2h dw 200h LIA_Current_XMit_Mask dw 0 LIA_Rec_Data_Full_Masks dw 4h dw 400h dw 1h dw 100h LIA_Current_Rec_Mask dw 0 Global_LIA_SEG_Value dw SEG _Global_LIA_Address ; ; This is used to store the address of the interupt status register to ; read for FRead and FWrite. Also we need to store the target timeout time ; in the same format as is returned from the get time function. ; LIA_Int_Status_Reg_Addr dw 0 LIA_Timeout_Hours db 0 LIA_Timeout_Mins db 0 LIA_Timeout_Secs db 0 LIA_Timeout_Hunds db 0 _DATA ENDS .CODE ; ; /******************************************************************** ; * * ; * Function : LIA_Write * ; * * ; * Purpose : This routine is used to write one word of data to * ; * the LIA as fast as possible. * ; * * ; * Parameters : ui_Module_No * ; * uint Module site to write to * ; * ul_Data_Value * ; * ulong value of data to write * ; * * ; * Returns : NONE * ; * * ; ********************************************************************/ ; ; void far LIA_Write( UINT ui_Module_No, ULONG ul_Data_Value ) ; ; Store all of the registers that we'll use later on. We don't have to ; worry about AX as thats what is used for the return value, which ; in our case is a void anyway. ; _LIA_Write proc far push bp mov bp,sp push dx push si push es ; ; We get the LIA base address into dx. This is used to write out the ; data to the two consequative locations (under control of bx). We then ; calculate which link to write it to before actually writing it. This ; is done using a lookup table defined at the start of the section. ; NOTE : we perform NO ERROR checking, ie we don't look at the fifo full ; bit to see whether we can actually write yet! ; LIA_Write_Main_Routine: mov es, word ptr Global_LIA_SEG_Value mov dx, es:_Global_LIA_Address mov si, ui_Module_No shl si, 1 add dx, word ptr LIA_Module_Offset_Table[si] mov ax, ul_Data_Value_Low out dx, ax add dx, i_LIA_Hi_Lo_Diff mov ax, ul_Data_Value_High out dx, ax ; Exit_LIA_Write: pop es pop si pop dx pop bp ret ; _LIA_Write endp ; ; ; /******************************************************************** ; * * ; * Function : LIA_Read * ; * * ; * Purpose : This routine is used to read one word from the * ; * LIA as fast as possible. * ; * * ; * Parameters : ui_Module_No * ; * uint Module site to read from * ; * *ul_Data_Value * ; * ulong where to place the data that was read * ; * * ; * Returns : NONE * ; * * ; ********************************************************************/ ; ; void far LIA_Read( UINT ui_Module_No, ULONG *ulp_Data_Value ) ; ; ; ; Store all of the registers that we'll use later on. We don't have to ; worry about AX as thats what is used for the return value, which ; in our case is a void anyway. ; _LIA_Read proc far push bp mov bp,sp push bx push es push dx push si ; ; We get the LIA base address into dx. This is used to read the ; data from the two consequative locations (under control of bx). We then ; calculate which link to write it to before actually writing it. This ; is done using a lookup table defined at the start of the section. ; NOTE : we perform NO ERROR checking, ie we don't look at the fifo full ; bit to see whether we can actually write yet! ; LIA_Read_Main_Routine: mov es, word ptr Global_LIA_SEG_Value mov dx, es:_Global_LIA_Address mov si, ui_Module_No shl si, 1 add dx, word ptr LIA_Module_Offset_Table[si] les bx, ulp_Data_Value_Offset in ax, dx mov es:[bx+0], ax add dx, i_LIA_Hi_Lo_Diff in ax, dx mov es:[bx+2], ax ; Exit_LIA_Read: pop si pop dx pop es pop bx pop bp ret ; _LIA_Read endp ; ; /******************************************************************** ; * * ; * Function : LIA_Write_Block * ; * * ; * Purpose : This routine is used to write a block of data to * ; * the LIA as fast as possible. * ; * * ; * Parameters : ui_Module_No * ; * uint Module site to write to * ; * ui_Number_to_Transfer * ; * uint Size of buffer to transfer * ; * *ul_Data_Value * ; * ulong value of data to write * ; * * ; * Returns : NONE * ; * * ; ********************************************************************/ ; ; void far LIA_Write_Block( UINT ui_Module_No, UINT ui_Number_to_Transfer, ULONG *ul_Data_Block ) ; ; Store all of the registers that we'll use later on. We don't have to ; worry about AX as thats what is used for the return value, which ; in our case is a void anyway. ; _LIA_Write_Block proc far push bp mov bp,sp push bx push es push cx push dx push si push di ; ; We use DX to access the two 16 bit data registers on the LIA. We need ; to access two consequative locations and we do this via DI and SI. We then ; calculate which link to write it to before actually writing it. This ; is done using a lookup table defined at the start of the section. ; We also push DS as this allows us to acccess the array as the default ; data segment. This is quicker than using es:[...] by 2 clocks. ; We use the following registers : ; AX - used for data transfer ; BX - used to step through the user data block ; CX - counter through the block ; DX - used to access the PC I/O bus ; DI - temporary register used to store the absolute location of ; the data register used to access the 16 LSBits (eg 0x290) ; SI - temporary register used to store the absolute location of ; the data register used to access the 16 MSBits (eg 0x292) ; ; NOTE : we perform NO ERROR checking, ie we don't look at the fifo full ; bit to see whether we can actually write yet! ; LIA_Write_Block_Main_Routine: mov es, word ptr Global_LIA_SEG_Value mov di, es:_Global_LIA_Address mov si, ui_Module_No shl si, 1 add di, word ptr LIA_Module_Offset_Table[si] mov si, di add si, i_LIA_Hi_Lo_Diff les bx, ul_Data_Block_Offset mov cx, ui_Number_to_Transfer push ds mov ax, es mov ds, ax ; ; We now have all the registers set up we can now actually perform our ; loop. This is controlled by cx. For speed advantages we have chosen to ; open out the loop and handle it as two separate sections. The section ; below is used to output the 'remainder' of words to bring the loop ; size down to a factor of eight. Thus, we get the bottom 3 bits of the ; counter and then write this amount of words. ; and cx, 7 je LIA_Write_Block_Counter_Load LIA_Write_Block_Odd_Loop: LIA_Write_One_Word 0 add bx, 4 dec cx jnz LIA_Write_Block_Odd_Loop ; ; Now the loop is a factor of 8 long. We therefore loop round writing ; 8 consequative words before we loop back. This saves us 18 clocks per ; word for 7 of the 8 encountered. A reasonable saving! ; ; NOTE: This might not be the absolute fastest method for all i86 ; architectures as the size of the block may be larger than the on-board ; cache. Thus the cache will need to be refreshed every loop, not good. ; LIA_Write_Block_Counter_Load: mov cx, ui_Number_to_Transfer and cx, 0fff8H je Exit_LIA_Write_Block_Counter_Load ; LIA_Write_Block_Loop: LIA_Write_One_Word 0 LIA_Write_One_Word 4 LIA_Write_One_Word 8 LIA_Write_One_Word 12 LIA_Write_One_Word 16 LIA_Write_One_Word 20 LIA_Write_One_Word 24 LIA_Write_One_Word 28 ; ; Once we have output all 8 words we need to move BX on by 8 ULONGS and ; CX by 8. Then we loop back if required. ; add bx, 32 sub cx, 8 jnz LIA_Write_Block_Loop ; ; If we have under 8 words to write then we need to ensure that DS is popped. ; Exit_LIA_Write_Block_Counter_Load: pop ds ; Exit_LIA_Write_Block: pop di pop si pop dx pop cx pop es pop bx pop bp ret ; _LIA_Write_Block endp ; ; ; /******************************************************************** ; * * ; * Function : LIA_Read_Block * ; * * ; * Purpose : This routine is used to read a block of data * ; * from the LIA as fast as possible. * ; * * ; * Parameters : ui_Module_No * ; * uint Module site to read from * ; * ui_Number_to_Transfer * ; * uint Size of buffer to transfer * ; * *ul_Data_Value * ; * ulong where to place the data that was read * ; * * ; * Returns : NONE * ; * * ; ********************************************************************/ ; ; void far LIA_Read_Block( UINT ui_Module_No, UINT ui_Number_to_Transfer, ULONG *ulp_Data_Value ) ; ; ; Store all of the registers that we'll use later on. We don't have to ; worry about AX as thats what is used for the return value, which ; in our case is a void anyway. ; _LIA_Read_Block proc far push bp mov bp,sp push bx push es push cx push dx push si push di ; ; We use DX to access the two 16 bit data registers on the LIA. We need ; to access two consequative locations and we do this via DI and SI. We then ; calculate which link to read it from before actually reading it. This ; is done using a lookup table defined at the start of the section. ; We also push DS as this allows us to acccess the array as the default ; data segment. This is quicker than using es:[...] by 2 clocks. ; We use the following registers : ; AX - used for data transfer ; BX - used to step through the user data block ; CX - counter through the block ; DX - used to access the PC I/O bus ; DI - temporary register used to store the absolute location of ; the data register used to access the 16 LSBits (eg 0x290) ; SI - temporary register used to store the absolute location of ; the data register used to access the 16 MSBits (eg 0x292) ; ; NOTE : we perform NO ERROR checking, ie we don't look at the fifo full ; bit to see whether we can actually write yet! ; LIA_Read_Block_Main_Routine: mov es, word ptr Global_LIA_SEG_Value mov di, es:_Global_LIA_Address mov si, ui_Module_No shl si, 1 add di, word ptr LIA_Module_Offset_Table[si] mov si, di add si, i_LIA_Hi_Lo_Diff les bx, ul_Data_Block_Offset mov cx, ui_Number_to_Transfer push ds mov ax, es mov ds, ax ; ; We now have all the registers set up we can now actually perform our ; loop. This is controlled by cx. For speed advantages we have chosen to ; open out the loop and handle it as two separate sections. The section ; below is used to input the 'remainder' of words to bring the loop ; size down to a factor of eight. Thus, we get the bottom 3 bits of the ; counter and then read this amount of words. ; and cx, 7 je LIA_Read_Block_Counter_Load LIA_Read_Block_Odd_Loop: LIA_Read_One_Word 0 add bx, 4 dec cx jnz LIA_Read_Block_Odd_Loop ; ; Now the loop is a factor of 8 long. We therefore loop round reading ; 8 consequative words before we loop back. This saves us 18 clocks per ; word for 7 of the 8 encountered. A reasonable saving! ; ; NOTE: This might not be the absolute fastest method for all i86 ; architectures as the size of the block may be larger than the on-board ; cache. Thus the cache will need to be refreshed every loop, not good. ; LIA_Read_Block_Counter_Load: mov cx, ui_Number_to_Transfer and cx, 0fff8H je Exit_LIA_Read_Block_Counter_Load ; LIA_Read_Block_Loop: LIA_Read_One_Word 0 LIA_Read_One_Word 4 LIA_Read_One_Word 8 LIA_Read_One_Word 12 LIA_Read_One_Word 16 LIA_Read_One_Word 20 LIA_Read_One_Word 24 LIA_Read_One_Word 28 ; ; Once we have input all 8 words we need to move BX on by 8 ULONGS and ; CX by 8. Then we loop back if required. ; add bx, 32 sub cx, 8 jnz LIA_Read_Block_Loop ; ; If we have under 8 words to read then we need to ensure that DS is popped. ; Exit_LIA_Read_Block_Counter_Load: pop ds ; Exit_LIA_Read_Block: pop di pop si pop dx pop cx pop es pop bx pop bp ret ; _LIA_Read_Block endp ; ; ; /******************************************************************** ; * * ; * Function : LIA_FWrite_Block * ; * * ; * Purpose : This routine is used to write a block of data to * ; * the LIA. However, if the C40 will not accept all * ; * of the data that the user wishes to send then * ; * this routine will time out and then inform the * ; * user of the number of words that were * ; * transferred. * ; * * ; * Parameters : ui_Module_No * ; * uint Module site to write to * ; * ui_Number_to_Transfer * ; * uint Size of buffer to transfer * ; * *ul_Data_Value * ; * ulong value of data to write * ; * ui_Timeout_Period * ; * uint timeout period in 1/100ths of a second * ; * (This has to be in the range 1..6000) * ; * * ; * Returns : The number of words actually written. * ; * * ; ********************************************************************/ ; ; UINT far LIA_FWrite_Block( UINT ui_Module_No, ; UINT ui_Number_to_Transfer, ; ULONG *ul_Data_Block, ; UINT ui_Timeout_Period) ; ; Store all of the registers that we'll use later on. We don't have to ; worry about AX as thats what is used for the return value, which ; in our case is a void anyway. ; Early_Exit_LIA_FWrite_Block: jmp Exit_LIA_FWrite_Block _LIA_FWrite_Block proc far push bp mov bp,sp push bx push es push cx push dx push si push di ; ; Before we do any real access check to see that ui_Timeout_Period is ; in the range 1..6000. Move the zero words written into AX. ; sub ax, ax mov bx, ui_Timeout_Period cmp bx, 0 je Early_Exit_LIA_FWrite_Block cmp bx, 06000 jg Early_Exit_LIA_FWrite_Block push bx ; ; At this point we have a valid Timeout period given to us by the ; user. What we must do next is to turn that into one that we can ; easily compare with. Now it is important to note at this time that ; we use the get time function (int 21h, fn44) to retrieve the ; time. This returns the minutes in CL, seconds in DH and hundreths ; in DL. Thus we only want to make direct comparisions with these ; and the user defined timeout (incremented by the current time), ; ie we want the user 1/100ths value in terms of minutes, seconds and ; hundreths! (Note AL has the quotient and AH the remainder). ; Once we have calculated it we store it as BYTES! ; ; So, first get the current time. Note that we push'ed the user Timeout ; period above, so we need to pop it into AX to work with. Then, we divide ; the value by 100, to get seconds and hundreths (stored in AL, AH ; respectively). When we have the current time we split the user requested ; number of 1/100ths up so that we can simply add them to the returned time. ; During the addition we need to ensure that we take account of any ; possible 'rollover' into the next digit, hence all of the additions and ; comparisions. ; mov ah, 2Ch int 21h pop ax mov bl, 0100 div bl add ah, dl cmp ah, 100 jl LIA_FWrite_No_Second_Rollover inc al sub ah, 100 LIA_FWrite_No_Second_Rollover: add al, dh cmp al, 60 jl LIA_FWrite_No_Minute_Rollover inc cl sub al, 60 LIA_FWrite_No_Minute_Rollover: cmp cl, 60 jl LIA_FWrite_No_Hour_Rollover inc ch sub cl, 60 ; ; Save the results. Also return the Number to transfer back into CX. ; LIA_FWrite_No_Hour_Rollover: mov LIA_Timeout_Hours, ch mov LIA_Timeout_Mins, cl mov LIA_Timeout_Secs, al mov LIA_Timeout_Hunds, ah ; ; We use DX to access the two 16 bit data registers on the LIA. We need ; to access two consequative locations and we do this via DI and SI. We then ; calculate which link to write it to before actually writing it. This ; is done using a lookup table defined at the start of the section. ; The mask to use to determine if we can send a bit is also determined and ; is stored in LIA_Current_XMit_Mask. Also store the Interupt Status ; register location to pole. ; We also push DS as this allows us to acccess the array as the default ; data segment. This is quicker than using es:[...] by 2 clocks. ; We use the following registers : ; AX - used for data transfer ; BX - used to step through the user data block ; CX - counter through the block ; DX - used to access the PC I/O bus ; DI - temporary register used to store the absolute location of ; the data register used to access the 16 LSBits (eg 0x290) ; SI - temporary register used to store the absolute location of ; the data register used to access the 16 MSBits (eg 0x292) ; ; NOTE : we perform NO ERROR checking, ie we don't look at the fifo full ; bit to see whether we can actually write yet! ; LIA_FWrite_Block_Main_Routine: mov cx, ui_Number_to_Transfer mov es, word ptr Global_LIA_SEG_Value mov ax, es:_Global_LIA_Address mov di, ax add ax, 012h mov word ptr LIA_Int_Status_Reg_Addr, ax mov si, ui_Module_No shl si, 1 add di, word ptr LIA_Module_Offset_Table[si] mov ax, word ptr LIA_XMit_Data_Empty_Masks[si] mov word ptr LIA_Current_XMit_Mask, ax mov si, di add si, i_LIA_Hi_Lo_Diff les bx, ul_Data_Block_Offset push ds mov ax, es mov ds, ax pop es push es ; ; We now have all the registers set up we can now actually perform our ; loop. The number of words to write stored in CX. ; LIA_FWrite_Block_Loop: push cx push bx LIA_FWrite_Wait_for_Timeout_or_OK_to_XMit: mov dx, es:[LIA_Int_Status_Reg_Addr] in ax, dx and ax, word ptr es:[LIA_Current_XMit_Mask] jnz LIA_Perform_Word_Write mov ah, 2Ch int 21h cmp ch, byte ptr es:[LIA_Timeout_Hours] jl LIA_FWrite_Wait_for_Timeout_or_OK_to_XMit cmp cl, byte ptr es:[LIA_Timeout_Mins] jl LIA_FWrite_Wait_for_Timeout_or_OK_to_XMit cmp dh, byte ptr es:[LIA_Timeout_Secs] jl LIA_FWrite_Wait_for_Timeout_or_OK_to_XMit cmp dl, byte ptr es:[LIA_Timeout_Hunds] jl LIA_FWrite_Wait_for_Timeout_or_OK_to_XMit ; ; TIMEOUT!!!! ; WE pop BX to move the stack on. Then we pop the number of words remaining ; to transfer into CX. This is then subtracted from he number that we ; started with to produce a return code. ; pop bx pop cx mov ax, ui_Number_to_Transfer sub ax, cx jmp Exit_LIA_FWrite_Block_from_Timeout LIA_Perform_Word_Write: pop bx pop cx LIA_Write_One_Word 0 ; ; Once we have output both words we move DX back to the low order 16 bit ; data register. We then move BX (which steps through the data block) on ; by 4 bytes, ie 1 ULONG. Then we loop back if required. ; When we finish we set AX to be 0 as we have NO words remaining to Xmit. ; add bx, 4 dec cx jnz LIA_FWrite_Block_Loop mov ax, ui_Number_to_Transfer Exit_LIA_FWrite_Block_from_Timeout: pop ds ; Exit_LIA_FWrite_Block: pop di pop si pop dx pop cx pop es pop bx pop bp ret _LIA_FWrite_Block endp ; ; ; /******************************************************************** ; * * ; * Function : LIA_FRead_Block * ; * * ; * Purpose : This routine is used to read a block of data from * ; * the LIA. However, if the C40 will not accept all * ; * of the data that the user wishes to send then * ; * this routine will time out and then inform the * ; * user of the number of words that were * ; * transferred. * ; * * ; * Parameters : ui_Module_No * ; * uint Module site to read from * ; * ui_Number_to_Transfer * ; * uint Size of buffer to transfer * ; * *ul_Data_Value * ; * ulong where to place the data that was read * ; * ui_Timeout_Period * ; * uint timeout period in 1/100ths of a second * ; * (This has to be in the range 1..6000) * ; * * ; * Returns : NONE * ; * * ; ********************************************************************/ ; ; UINT far LIA_FRead_Block( UINT ui_Module_No, ; UINT ui_Number_to_Transfer, ; ULONG *ulp_Data_Value, ; UINT ui_Timeout_Period) ; ; ; Store all of the registers that we'll use later on. We don't have to ; worry about AX as thats what is used for the return value, which ; in our case is a void anyway. ; ; Early_Exit_LIA_FRead_Block: jmp Exit_LIA_FRead_Block _LIA_FRead_Block proc far push bp mov bp,sp push bx push es push cx push dx push si push di ; ; Before we do any real access check to see that ui_Timeout_Period is ; in the range 1..6000. Move the zero words written into AX to return. ; sub ax, ax mov bx, ui_Timeout_Period cmp bx, 0 je Early_Exit_LIA_FRead_Block cmp bx, 06000 jg Early_Exit_LIA_FRead_Block push bx ; ; At this point we have a valid Timeout period given to us by the ; user. What we must do next is to turn that into one that we can ; easily compare with. Now it is important to note at this time that ; we use the get time function (int 21h, fn44) to retrieve the ; time. This returns the minutes in CL, seconds in DH and hundreths ; in DL. Thus we only want to make direct comparisions with these ; and the user defined timeout (incremented by the current time), ; ie we want the user 1/100ths value in terms of minutes, seconds and ; hundreths! (Note AL has the quotient and AH the remainder). ; Once we have calculated it we store it as BYTES! ; ; So, first get the current time. Note that we push'ed the user Timeout ; period above, so we need to pop it into AX to work with. Then, we divide ; the value by 100, to get seconds and hundreths (stored in AL, AH ; respectively). When we have the current time we split the user requested ; number of 1/100ths up so that we can simply add them to the returned time. ; During the addition we need to ensure that we take account of any ; possible 'rollover' into the next digit, hence all of the additions and ; comparisions. ; mov ah, 2Ch int 21h pop ax mov bl, 0100 div bl add ah, dl cmp ah, 100 jl LIA_FRead_No_Second_Rollover inc al sub ah, 100 LIA_FRead_No_Second_Rollover: add al, dh cmp al, 60 jl LIA_FRead_No_Minute_Rollover inc cl sub al, 60 LIA_FRead_No_Minute_Rollover: cmp cl, 60 jl LIA_FRead_No_Hour_Rollover inc ch sub cl, 60 ; ; Save the results. Also return the Number to transfer back into CX. ; LIA_FRead_No_Hour_Rollover: mov LIA_Timeout_Hours, ch mov LIA_Timeout_Mins, cl mov LIA_Timeout_Secs, al mov LIA_Timeout_Hunds, ah ; ; We use DX to access the two 16 bit data registers on the LIA. We need ; to access two consequative locations and we do this via DI and SI. We then ; calculate which link to write it to before actually writing it. This ; is done using a lookup table defined at the start of the section. ; The mask to use to determine if we can send a bit is also determined and ; is stored in LIA_Current_Rec_Mask. Also store the Interupt Status ; register location to pole. ; We also push DS as this allows us to acccess the array as the default ; data segment. This is quicker than using es:[...] by 2 clocks. ; We use the following registers : ; AX - used for data transfer ; BX - used to step through the user data block ; CX - counter through the block ; DX - used to access the PC I/O bus ; DI - temporary register used to store the absolute location of ; the data register used to access the 16 LSBits (eg 0x290) ; SI - temporary register used to store the absolute location of ; the data register used to access the 16 MSBits (eg 0x292) ; ; NOTE : we perform NO ERROR checking, ie we don't look at the fifo full ; bit to see whether we can actually write yet! ; LIA_FRead_Block_Main_Routine: mov cx, ui_Number_to_Transfer mov es, word ptr Global_LIA_SEG_Value mov ax, es:_Global_LIA_Address mov di, ax add ax, 012h mov word ptr LIA_Int_Status_Reg_Addr, ax mov si, ui_Module_No shl si, 1 add di, word ptr LIA_Module_Offset_Table[si] mov ax, word ptr LIA_Rec_Data_Full_Masks[si] mov word ptr LIA_Current_Rec_Mask, ax mov si, di add si, i_LIA_Hi_Lo_Diff les bx, ul_Data_Block_Offset push ds mov ax, es mov ds, ax pop es push es ; ; We now have all the registers set up we can now actually perform our ; loop. The number of words to write stored in CX. ; LIA_FRead_Block_Loop: push cx push bx LIA_FRead_Wait_for_Timeout_or_OK_to_XMit: mov dx, es:[LIA_Int_Status_Reg_Addr] in ax, dx and ax, word ptr es:[LIA_Current_Rec_Mask] jnz LIA_Perform_Word_Read mov ah, 2Ch int 21h cmp ch, byte ptr es:[LIA_Timeout_Hours] jl LIA_FRead_Wait_for_Timeout_or_OK_to_XMit cmp cl, byte ptr es:[LIA_Timeout_Mins] jl LIA_FRead_Wait_for_Timeout_or_OK_to_XMit cmp dh, byte ptr es:[LIA_Timeout_Secs] jl LIA_FRead_Wait_for_Timeout_or_OK_to_XMit cmp dl, byte ptr es:[LIA_Timeout_Hunds] jl LIA_FRead_Wait_for_Timeout_or_OK_to_XMit ; ; TIMEOUT!!!! ; WE pop BX to move the stack on. Then we pop the number of words remaining ; to transfer into CX. This is subtracted from the number that we started ; with to return the number of words actually written. ; pop bx pop cx mov ax, ui_Number_to_Transfer sub ax, cx jmp Exit_LIA_FRead_Block_from_Timeout LIA_Perform_Word_Read: pop bx pop cx LIA_Read_One_Word 0 ; ; Once we have output both words we move DX back to the low order 16 bit ; data register. We then move BX (which steps through the data block) on ; by 4 bytes, ie 1 ULONG. Then we loop back if required. ; When we finish we set AX to be 0 as we have NO words remaining to Xmit. ; add bx, 4 dec cx jnz LIA_FRead_Block_Loop mov ax, ui_Number_to_Transfer Exit_LIA_FRead_Block_from_Timeout: pop ds ; Exit_LIA_FRead_Block: pop di pop si pop dx pop cx pop es pop bx pop bp ret _LIA_FRead_Block endp end