Minix1.5/kernel/klib88.x
| This file contains a number of assembly code utility routines needed by the
| kernel. They are:
.define _bios13 | make BIOS 13 call for disk I/O (real mode only)
.define _build_sig | build 4 word structure pushed onto stack for signals
.define _check_mem | check a block of memory, return the valid size
.define _cim_at_wini | clear the AT winchester interrupt mask
.define _cim_floppy | clear the floppy interrupt mask
.define _cim_printer | clear the printer interrupt mask
.define _cim_xt_wini | clear the XT winchester interrupt mask
.define _cp_mess | copies messages from source to destination
.define _exit | dummy for library routines
.define .fat | dummy for library routines
.define _get_byte | read a byte from anywhere and return it
.define _in_byte | read a byte from a port and return it
.define _klib_1hook | init from real mode for real or protected mode
.define _klib_2hook | init from protected mode for real or protected mode
.define _lock | disable interrupts
.define _out_byte | write a byte to a port
.define _phys_copy | copy data from anywhere to anywhere in memory
.define _port_read | transfer data from (disk controller) port to memory
.define _port_write | transfer data from memory to (disk controller) port
.define _reset | reset the system (real mode only)
.define _scr_down | scroll screen a line down (in software, by copying)
.define _scr_up | scroll screen a line up (in software, by copying)
.define _sim_printer | set the printer interrupt mask
.define _tasim_printer | test and set the printer interrupt mask
.define _test_and_set | test and set locking primitive on a word in memory
.define .trp | dummy for library routines
.define _unlock | enable interrupts
.define _vid_copy | copy data to video ram (perhaps during retrace only)
.define _wait_retrace | wait for retrace interval
| The routines only guarantee to preserve the registers the 'C' compiler
| expects to be preserved (si, di, bp, sp, segment registers, and direction
| bit in the flags), though some of the older ones preserve bx, cx and dx.
#include <minix/config.h>
#include <minix/const.h>
#include "const.h"
#include "sconst.h"
#include "protect.h"
#if INTEL_32BITS
#error /* this is not the 32-bit version */
#endif
#define DS_286_OFFSET DS_286_INDEX*DESC_SIZE
#define ES_286_OFFSET ES_286_INDEX*DESC_SIZE
#define EM_MASK 0xFFF0 /* extended memory mask for hi word */
#define EM_XFER_VEC 0x15 /* copy (normal or extended) memory */
# define EM_XFER_FUNC 0x87
#define JMP_OPCODE 0xE9 /* opcode used for patching */
#define OFF_MASK 0x000F /* offset mask for phys_b -> hclick:offset */
| imported functions
.extern p_restart
.extern p_save
.extern _restart
.extern save
| exported variables
.bss
.define splimit
| imported variables
.extern _Ax, _Bx, _Cx, _Dx, _Es
.extern _blank_color
.extern _gdt
.extern _protected_mode
.extern _snow
.extern _vec_table
.extern _vid_mask
.extern _vid_port
.text
|*===========================================================================*
|* bios13 *
|*===========================================================================*
| PUBLIC void bios13();
_bios13: | make a BIOS 0x13 call for disk I/O
push ax | save the registers
push bx
push cx
push dx
push es
mov ax,_Ax | load parameters
mov bx,_Bx
mov cx,_Cx
mov dx,_Dx
mov es,_Es
int 0x13 | make the BIOS call
mov _Ax,ax | save results
mov _Bx,bx
mov _Cx,cx
mov _Dx,dx
pop es
pop dx
pop cx
pop bx
pop ax
ret
|*===========================================================================*
|* build_sig *
|*===========================================================================*
| PUBLIC void build_sig(char *sig_stuff, struct proc *rp, int sig)
| Build a structure that is pushed onto the stack for signals. It contains
| pc, psw, etc., and is machine dependent. The format is the same as generated
| by hardware interrupts, except that after the "interrupt", the signal number
| is also pushed. The signal processing routine within the user space first
| pops the signal number, to see which function to call. Then it calls the
| function. Finally, when the function returns to the low-level signal
| handling routine, control is passed back to where it was prior to the signal
| by executing a return-from-interrupt instruction, hence the need for using
| the hardware generated interrupt format on the stack.
_build_sig:
push bp | save bp
mov bp,sp | set bp to sp for accessing params
push bx | save bx
push si | save si
mov bx,4(bp) | bx points to sig_stuff
mov si,6(bp) | si points to proc table entry
mov ax,8(bp) | ax = signal number
mov (bx),ax | put signal number in sig_stuff
mov ax,PCREG(si) | ax = signalled process' PC
mov 2(bx),ax | put pc in sig_stuff
mov ax,CSREG(si) | ax = signalled process' cs
mov 4(bx),ax | put cs in sig_stuff
mov ax,PSWREG(si) | ax = signalled process' PSW
mov 6(bx),ax | put psw in sig_stuff
pop si | restore si
pop bx | restore bx
pop bp | restore bp
ret | return to caller
|*===========================================================================*
|* check_mem *
|*===========================================================================*
| PUBLIC phys_bytes check_mem(phys_bytes base, phys_bytes size);
| Check a block of memory, return the amount valid.
| Only every 16th byte is checked.
| The memory and initial size must be <= 1M for non-protected mode.
| An initial size of 0 means everything.
| This really should do some alias checks.
_check_mem:
push bp
mov bp,sp
push ds
mov dx,4+2(bp) | base in dx:ax
sub ax,ax | prepare for early exit
test dx,#notop(HCHIGH_MASK)
jnz cm_1exit | can't handle bases above 1M
mov ax,4(bp) | ax = base segment = base / 16 % 0x10000
andb al,#HCLOW_MASK
orb al,dl
movb cl,#HCLICK_SHIFT
ror ax,cl
mov bx,4+4(bp) | size in dx:bx
mov dx,4+4+2(bp)
test dx,#notop(HCHIGH_MASK)
jz over_cm_reduce
movb dl,#HCHIGH_MASK
mov bx,#0xFFFF
over_cm_reduce:
andb bl,#HCLOW_MASK | cx = size in hclicks = size / 16 % 0x10000
orb bl,dl
ror bx,cl
mov cx,bx
push cx | save size in clicks
mov bx,4(bp) | bx = base offset = base % 16
and bx,#OFF_MASK
cm_loop:
mov ds,ax
movb dl,#TEST1PATTERN
xchgb dl,(bx) | write test pattern, remember original value
xchgb dl,(bx) | restore original value, read test pattern
cmpb dl,#TEST1PATTERN | must agree if good real memory
jnz cm_exit | if different, memory is unusable
movb dl,#TEST2PATTERN
xchgb dl,(bx)
xchgb dl,(bx)
cmpb dl,#TEST2PATTERN
jnz cm_exit
inc ax | next segment, test for wraparound at 1M
loopnz cm_loop
cm_exit:
pop ax
sub ax,cx | verified size in phys_clicks
cm_1exit:
movb dl,ah | convert to phys_bytes in dx:ax
movb cl,#HCLICK_SHIFT
shl ax,cl
shr dx,cl
and dx,#HCHIGH_MASK
pop ds
pop bp
ret
|*===========================================================================*
|* cim_at_wini *
|* cim_floppy *
|* cim_printer *
|* cim_xt_wini *
|*===========================================================================*
| All these routines are meant to be called from the task level where
| interrupts should not be globally disabled, so they return with interrupts
| enabled.
| PUBLIC void cim_at_wini();
| Clear the AT winchester interrupt mask.
_cim_at_wini:
cli
in INT2_MASK
andb al,#notop(AT_WINI_MASK)
out INT2_MASK
sti
ret
| PUBLIC void cim_floppy();
| Clear the AT winchester interrupt mask.
_cim_floppy:
cli
in INT_CTLMASK
andb al,#notop(FLOPPY_MASK)
out INT_CTLMASK
sti
ret
| PUBLIC void cim_printer();
| Clear the printer interrupt mask.
_cim_printer:
cli
in INT_CTLMASK
#if ASLD
andb al,#notop(PRINTER_MASK)
#else
andb al,#notop(PRINTER_MASK) & 0xFF
#endif
out INT_CTLMASK
sti
ret
| PUBLIC void cim_xt_wini();
| Clear the xt_wini interrupt mask.
_cim_xt_wini:
cli
in INT_CTLMASK
andb al,#notop(XT_WINI_MASK)
out INT_CTLMASK
sti
ret
|*===========================================================================*
|* cp_mess *
|*===========================================================================*
| PUBLIC void cp_mess(int src, phys_clicks src_clicks, vir_bytes src_offset,
| phys_clicks dst_clicks, vir_bytes dst_offset);
| This routine makes a fast copy of a message from anywhere in the address
| space to anywhere else. It also copies the source address provided as a
| parameter to the call into the first word of the destination message.
|
| Note that the message size, 'Msize' is in WORDS (not bytes) and must be set
| correctly. Changing the definition of message in the type file and not
| changing it here will lead to total disaster.
_cp_mess:
push es | save es
push ds | save ds
mov bx,sp | index off bx because machine can't use sp
push si | save si
push di | save di
mov ax,12(bx) | destination click
#if HCLICK_SHIFT > CLICK_SHIFT
#error /* Small click sizes are not supported (right shift will lose bits). */
#endif
#if HCLICK_SHIFT < CLICK_SHIFT
movb cl,#CLICK_SHIFT-HCLICK_SHIFT
shl ax,cl | destination segment
#endif
mov es,ax
mov di,14(bx) | offset of destination message
| Be careful not to destroy ds before we're finished with the bx pointer.
| We're using bx and not the more natural bp to save pushing bp.
mov ax,6(bx) | process number of sender
mov si,10(bx) | offset of source message
mov bx,8(bx) | source click (finished with bx as a pointer)
#if HCLICK_SHIFT < CLICK_SHIFT
shl bx,cl | source segment
#endif
mov ds,bx
stow | copy sender's process number to dest message
add si,*2 | don't copy first word
mov cx,*Msize-1 | remember, first word doesn't count
rep | iterate cx times to copy 11 words
movw | copy the message
pop di | restore di
pop si | restore si
pop ds | restore ds
pop es | restore es
ret | that's all folks!
|*===========================================================================*
|* exit *
|*===========================================================================*
| PUBLIC void exit();
| Some library routines use exit, so provide a dummy version.
| Actual calls to exit cannot occur in the kernel.
| Same for .fat
| Same for .trp.
_exit:
.fat:
.trp:
sti
j _exit
|*===========================================================================*
|* get_byte *
|*===========================================================================*
| PUBLIC u16_t get_byte(u16_t segment, 16_t *offset);
| Load and return the byte at the far pointer segment:offset.
_get_byte:
mov cx,ds | save ds
pop dx | return adr
pop ds | segment
pop bx | offset
sub sp,#2+2 | adjust for parameters popped
movb al,(bx) | load the byte to return
xorb ah,ah |
mov ds,cx | restore ds
jmp (dx) | return
|*===========================================================================*
|* in_byte *
|*===========================================================================*
| PUBLIC unsigned in_byte(port_t port);
| Read an (unsigned) byte from the i/o port port and return it.
_in_byte:
pop bx
pop dx | port
dec sp
dec sp
in | input 1 byte
subb ah,ah | unsign extend
jmp (bx)
|*===========================================================================*
|* klib_1hook *
|*===========================================================================*
| PUBLIC void klib_1hook();
| Initialize klib from real mode for real or protected mode,
| Do nothing for real mode.
| For protected mode, patch some real mode functions at their starts to jump
| to their protected mode equivalents, according to the patch table.
_klib_1hook:
cmpb _protected_mode,#0
jz hook1_done
mov si,#patch_table
lodw | original function
patch1:
mov bx,ax
seg cs
movb (bx),#JMP_OPCODE
lodw | new function - target of jump
sub ax,bx | relative jump
sub ax,#3 | adjust by length of jump instruction
seg cs
mov 1(bx),ax
lodw | next original function
test ax,ax
jnz patch1
hook1_done:
ret
|*===========================================================================*
|* klib_2hook *
|*===========================================================================*
| PUBLIC void klib_prot_mode_init();
| Initialize klib protected real mode for real or protected mode,
| Do nothing for real mode.
| For protected mode, load idt, task reg and flags.
_klib_2hook:
cmpb _protected_mode,#0
jz hook2_done
deflidt (_gdt+BIOS_IDT_SELECTOR) | loaded by BIOS, but in wrong mode!
mov ax,#TSS_SELECTOR | no other TSS is used
defltrax
sub ax,ax | zero
push ax | set flags to known good state
popf | especially, clear nested task and int enable
hook2_done:
ret
|*===========================================================================*
|* lock *
|*===========================================================================*
| PUBLIC void lock();
| Disable CPU interrupts.
_lock:
cli | disable interrupts
ret | return to caller
|*===========================================================================*
|* phys_copy *
|*===========================================================================*
| PUBLIC void phys_copy(phys_bytes source, phys_bytes destination,
| phys_bytes bytecount);
| Copy a block of physical memory.
SRCLO = 4
SRCHI = 6
DESTLO = 8
DESTHI = 10
COUNTLO = 12
COUNTHI = 14
_phys_copy:
push bp | save only registers required by C
mov bp,sp | set bp to point to source arg less 4
| check for extended memory
mov ax,SRCHI(bp)
or ax,DESTHI(bp)
test ax,#EM_MASK
jnz to_em_xfer
push si | save si
push di | save di
push ds | save ds
push es | save es
mov ax,SRCLO(bp) | dx:ax = source address (dx is NOT segment)
mov dx,SRCHI(bp)
mov si,ax | si = source offset = address % 16
and si,#OFF_MASK
| andb dl,#HCHIGH_MASK | ds = source segment = address / 16 % 0x10000
| mask is unnecessary because of EM_MASK test
andb al,#HCLOW_MASK
orb al,dl | now bottom 4 bits of dx are in ax
movb cl,#HCLICK_SHIFT | rotate them to the top 4
ror ax,cl
mov ds,ax
mov ax,DESTLO(bp) | dx:ax = destination addr (dx is NOT segment)
mov dx,DESTHI(bp)
mov di,ax | di = dest offset = address % 16
and di,#OFF_MASK
| andb dl,#HCHIGH_MASK | es = dest segment = address / 16 % 0x10000
andb al,#HCLOW_MASK
orb al,dl
ror ax,cl
mov es,ax
mov ax,COUNTLO(bp) | dx:ax = remaining count
mov dx,COUNTHI(bp)
| copy upwards (can't handle overlapped copy)
pc_loop:
mov cx,ax | provisional count for this iteration
test ax,ax | if count >= 0x8000, only do 0x8000 per iter
js pc_bigcount | low byte already >= 0x8000
test dx,dx
jz pc_upcount | less than 0x8000
pc_bigcount:
mov cx,#0x8000 | use maximum count per iteration
pc_upcount:
sub ax,cx | update count
sbb dx,#0 | can't underflow, so carry clear now for rcr
rcr cx,#1 | count in words, carry remembers if byte
jnc pc_even | no odd byte
movb | copy odd byte
pc_even:
rep | copy 1 word at a time
movw | word copy
mov cx,ax | test if remaining count is 0
or cx,dx
jnz pc_more | more to do
pop es | restore es
pop ds | restore ds
pop di | restore di
pop si | restore si
pop bp | restore bp
ret | return to caller
pc_more:
sub si,#0x8000 | adjust pointers so the offset doesn't
mov cx,ds | overflow in the next 0x8000 bytes
add cx,#0x800 | pointers end up same physical location
mov ds,cx | the current offsets are known >= 0x8000
sub di,#0x8000 | since we just copied that many
mov cx,es
add cx,#0x800
mov es,cx
j pc_loop | start next iteration
| When source or target is above 1M, join em_xfer.
| Rely on MM and MEMTASK not to provide bad parameters, and omit checking the
| following:
| count must be even
| count must be < 64K
| machine must be AT-compatible
| which are not required for phys_copy.
to_em_xfer:
shr COUNTLO(bp),#1 | convert count to words
pop bp | stack frame now agrees with em_xfer's
j _em_xfer
|*===========================================================================*
|* em_xfer *
|*===========================================================================*
| This file contains one routine which transfers words between user memory
| and extended memory on an AT or clone. A BIOS call (INT 15h, Func 87h)
| is used to accomplish the transfer. The BIOS call is "faked" by pushing
| the processor flags on the stack and then doing a far call through the
| saved vector table to the actual BIOS location. An actual INT 15h would
| get a MINIX complaint from an unexpected trap.
| This particular BIOS routine runs with interrupts off since the 80286
| must be placed in protected mode to access the memory above 1 Mbyte.
| So there should be no problems using the BIOS call, except it may take
| too long and cause interrupts to be lost.
|
.text
gdt: | Begin global descriptor table
| Dummy descriptor
.word 0 | segment length (limit)
.word 0 | bits 15-0 of physical address
.byte 0 | bits 23-16 of physical address
.byte 0 | access rights byte
.word 0 | reserved
| descriptor for GDT itself
.word 0 | segment length (limit)
.word 0 | bits 15-0 of physical address
.byte 0 | bits 23-16 of physical address
.byte 0 | access rights byte
.word 0 | reserved
src: | source descriptor
srcsz: .word 0 | segment length (limit)
srcl: .word 0 | bits 15-0 of physical address
srch: .byte 0 | bits 23-16 of physical address
.byte 0x93 | access rights byte
.word 0 | reserved
tgt: | target descriptor
tgtsz: .word 0 | segment length (limit)
tgtl: .word 0 | bits 15-0 of physical address
tgth: .byte 0 | bits 23-16 of physical address
.byte 0x93 | access rights byte
.word 0 | reserved
| BIOS CS descriptor
.word 0 | segment length (limit)
.word 0 | bits 15-0 of physical address
.byte 0 | bits 23-16 of physical address
.byte 0 | access rights byte
.word 0 | reserved
| stack segment descriptor
.word 0 | segment length (limit)
.word 0 | bits 15-0 of physical address
.byte 0 | bits 23-16 of physical address
.byte 0 | access rights byte
.word 0 | reserved
|
|
| Execute a transfer between user memory and extended memory.
|
| status = em_xfer(source, dest, count);
|
| Where:
| status => return code (0 => OK)
| source => Physical source address (32-bit)
| dest => Physical destination address (32-bit)
| count => Number of words to transfer
|
|
|
_em_xfer:
push bp | Save registers
mov bp,sp
push si
push es
push cx
|
| Pick up source and destination addresses and update descriptor tables
|
mov ax,4(bp)
seg cs
mov srcl,ax
mov ax,6(bp)
seg cs
movb srch,al
mov ax,8(bp)
seg cs
mov tgtl,ax
mov ax,10(bp)
seg cs
movb tgth,al
|
| Update descriptor table segment limits
|
mov cx,12(bp)
mov ax,cx
add ax,ax
seg cs
mov tgtsz,ax
seg cs
mov srcsz,ax
|
| Now do actual DOS call
|
push cs
pop es
mov si,#gdt
movb ah,#EM_XFER_FUNC
pushf | fake interrupt
calli @_vec_table+4*EM_XFER_VEC
|
| All done, return to caller.
|
pop cx | restore registers
pop es
pop si
mov sp,bp
pop bp
ret
|*===========================================================================*
|* out_byte *
|*===========================================================================*
| PUBLIC void out_byte(port_t port, int value);
| Write value (cast to a byte) to the I/O port port.
_out_byte:
pop bx
pop dx | port
pop ax | value
sub sp,#2+2
out | output 1 byte
jmp (bx)
|*===========================================================================*
|* port_read *
|*===========================================================================*
| PUBLIC void port_read(port_t port, phys_bytes destination,unsigned bytcount);
| Transfer data from (hard disk controller) port to memory.
_port_read:
push bp
mov bp,sp
push cx
push dx
push di
push es
mov ax,4+2(bp) | destination addr in dx:ax
mov dx,4+2+2(bp)
mov di,ax | di = dest offset = address % 16
and di,#OFF_MASK
andb dl,#HCHIGH_MASK | es = dest segment = address / 16 % 0x10000
andb al,#HCLOW_MASK
orb al,dl
movb cl,#HCLICK_SHIFT
ror ax,cl
mov es,ax
mov cx,4+2+4(bp) | count in bytes
shr cx,#1 | count in words
mov dx,4(bp) | port to read from
rep
insw
pop es
pop di
pop dx
pop cx
mov sp,bp
pop bp
ret
|*===========================================================================*
|* port_write *
|*===========================================================================*
| PUBLIC void port_write(port_t port, phys_bytes source, unsigned bytcount);
| Transfer data from memory to (hard disk controller) port.
_port_write:
push bp
mov bp,sp
push cx
push dx
push si
push ds
mov ax,4+2(bp) | source addr in dx:ax
mov dx,4+2+2(bp)
mov si,ax | si = source offset = address % 16
and si,#OFF_MASK
andb dl,#HCHIGH_MASK | ds = source segment = address / 16 % 0x10000
andb al,#HCLOW_MASK
orb al,dl
movb cl,#HCLICK_SHIFT
ror ax,cl
mov ds,ax
mov cx,4+2+4(bp) | count in bytes
shr cx,#1 | count in words
mov dx,4(bp) | port to read from
rep
outsw
pop ds
pop si
pop dx
pop cx
mov sp,bp
pop bp
ret
|*===========================================================================*
|* reset *
|*===========================================================================*
| PUBLIC void reset();
| Reset the system.
| This only works in real mode.
| For protected mode, it would be necessary to trap to privilege 0, then do
| something fatal like loading an IDT with offset 0 and interrupting.
_reset:
jmpi 0,0xFFFF
|*===========================================================================*
|* scr_down & scr_up *
|*===========================================================================*
| PUBLIC void scr_down(unsigned videoseg, int source, int dest, int count);
| Scroll the screen down one line.
|
| PUBLIC void scr_up(unsigned videoseg, int source, int dest, int count);
| Scroll the screen up one line.
|
| These are identical except scr_down() must reverse the direction flag
| during the copy to avoid problems with overlap.
_scr_down:
std
_scr_up:
push bp
mov bp,sp
push si
push di
push ds
push es
mov ax,4(bp) | videoseg (selector for video ram)
mov si,6(bp) | source (offset within video ram)
mov di,8(bp) | dest (offset within video ram)
mov cx,10(bp) | count (in words)
mov ds,ax | set source and dest segs to videoseg
mov es,ax
rep | do the copy
movw
pop es
pop ds
pop di
pop si
pop bp
cld | restore (unnecessarily for scr_up)
ret
|*===========================================================================*
|* sim_printer *
|*===========================================================================*
| PUBLIC void sim_printer();
| Set the printer interrupt mask.
| This is meant to be called from the task level, so it returns with
| interrupts enabled.
_sim_printer:
cli
in INT_CTLMASK
orb al,#PRINTER_MASK
out INT_CTLMASK
sti
ret
|*===========================================================================*
|* tasim_printer *
|*===========================================================================*
| PUBLIC unsigned tasim_printer();
| Set the printer interrupt mask, indivisibly with getting its old value.
| Return old value.
| This is meant to be called from the task level, so it returns with
| interrupts enabled.
| This might not work for multiple processors, unlike test_and_set().
_tasim_printer:
cli
in INT_CTLMASK
movb ah,al
orb al,#PRINTER_MASK
out INT_CTLMASK
sti
movb al,ah
and ax,#PRINTER_MASK
ret
|*===========================================================================*
|* test_and_set *
|*===========================================================================*
| PUBLIC int test_and_set(int *flag);
| Set the flag to TRUE, indivisibly with getting its old value.
| Return old flag.
_test_and_set:
pop dx
pop bx
sub sp,#2
mov ax,#1
xchg ax,(bx)
jmp (dx)
|*===========================================================================*
|* unlock *
|*===========================================================================*
| PUBLIC void unlock();
| Enable CPU interrupts.
_unlock:
sti | enable interrupts
ret | return to caller
|*===========================================================================*
|* vid_copy *
|*===========================================================================*
| PUBLIC void vid_copy(char *buffer, unsigned videobase, int offset,
| int words);
| where
| 'buffer' is a pointer to the (character, attribute) pairs
| 'videobase' is 0xB800 for color and 0xB000 for monochrome displays
| 'offset' tells where within video ram to copy the data
| 'words' tells how many words to copy
| if buffer is zero, the fill char (blank_color) is used
|
| This routine takes a string of (character, attribute) pairs and writes them
| onto the screen. For a snowy display, the writing only takes places during
| the vertical retrace interval, to avoid displaying garbage on the screen.
_vid_copy:
push bp | we need bp to access the parameters
mov bp,sp | set bp to sp for indexing
push si | save the registers
push di | save di
push bx | save bx
push cx | save cx
push dx | save dx
push es | save es
vid.0: mov si,4(bp) | si = pointer to data to be copied
mov es,6(bp) | load es NOW: int routines may NOT ruin it
mov di,8(bp) | di = offset within video ram
and di,_vid_mask | only 4K or 16K counts
mov cx,10(bp) | cx = word count for copy loop
mov dx,#0x3DA | prepare to see if color display is retracing
mov bx,di | see if copy will run off end of video ram
add bx,cx | compute where copy ends
add bx,cx | bx = last character copied + 1
sub bx,_vid_mask | bx = # characters beyond end of video ram
sub bx,#1 | note: dec bx doesn't set flags properly
| it DOES for jle!!
jle vid.1 | jump if no overrun
sar bx,#1 | bx = # words that don't fit in video ram
sub cx,bx | reduce count by overrun
vid.1: push cx | save actual count used for later
cmpb _snow,#0 | skip vertical retrace test if no snow
jz vid.4
|vid.2: in | with a color display, you can only copy to
| test al,*010 | the video ram during vertical retrace, so
| jnz vid.2 | wait for start of retrace period. Bit 3 of
vid.3: in | 0x3DA is set during retrace. First wait
testb al,*010 | until it is off (no retrace), then wait
jz vid.3 | until it comes on (start of retrace)
vid.4: cmp si,#0 | si = 0 means blank the screen
je vid.7 | jump for blanking
lock | this is a trick for the IBM PC simulator only
inc vidlock | 'lock' indicates a video ram access
rep | this is the copy loop
movw | ditto
vid.5: pop si | si = count of words copied
cmp bx,#0 | if bx < 0, then no overrun and we are done
jle vid.6 | jump if everything fit
mov 10(bp),bx | set up residual count
mov 8(bp),#0 | start copying at base of video ram
cmp 4(bp),#0 | NIL_PTR means store blanks
je vid.0 | go do it
add si,si | si = count of bytes copied
add 4(bp),si | increment buffer pointer
j vid.0 | go copy some more
vid.6: pop es | restore registers
pop dx | restore dx
pop cx | restore cx
pop bx | restore bx
pop di | restore di
pop si | restore si
pop bp | restore bp
ret | return to caller
vid.7: mov ax,_blank_color | ax = blanking character
rep | copy loop
stow | blank screen
j vid.5 | done
|*===========================================================================*
|* wait_retrace *
|*===========================================================================*
| PUBLIC void wait_retrace();
| Wait for the *start* of retrace period.
| The VERTICAL_RETRACE_MASK of the color vid_port is set during retrace.
| First wait until it is off (no retrace).
| Then wait until it comes on (start of retrace).
| We can't afford to worry about interrupts.
_wait_retrace:
mov dx,_vid_port
orb dl,#COLOR_STATUS_PORT & 0xFF
wait_no_retrace:
in
testb al,#VERTICAL_RETRACE_MASK
jnz wait_no_retrace
wait_retrace:
in
testb al,#VERTICAL_RETRACE_MASK
jz wait_retrace
ret
|*===========================================================================*
|* variants for protected mode *
|*===========================================================================*
| Some routines are different in protected mode.
| The only essential difference is the handling of segment registers.
| One complication is that the method of building segment descriptors is not
| reentrant, so the protected mode versions must not be called by interrupt
| handlers.
|*===========================================================================*
|* p_check_mem *
|*===========================================================================*
PCM_DENSITY = 256 | resolution of check
| the shift logic depends on this being 256
p_check_mem:
pop bx
pop _gdt+DS_286_OFFSET+DESC_BASE
pop ax | pop base into base of source descriptor
movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,al
pop cx | byte count in dx:cx
pop dx
sub sp,#4+4
push bx
push ds
sub ax,ax | prepare for early exit
test dx,#0xFF00
jnz pcm_1exit | can't handle bases above 16M
movb cl,ch | divide size by 256 and discard high byte
movb ch,dl
push cx | save divided size
sub bx,bx | test bytes at bases of segments
pcm_loop:
mov ax,#DS_286_SELECTOR
mov ds,ax
movb dl,#TEST1PATTERN
xchgb dl,(bx) | write test pattern, remember original value
xchgb dl,(bx) | restore original value, read test pattern
cmpb dl,#TEST1PATTERN | must agree if good real memory
jnz pcm_exit | if different, memory is unusable
movb dl,#TEST2PATTERN
xchgb dl,(bx)
xchgb dl,(bx)
cmpb dl,#TEST2PATTERN
jnz pcm_exit
seg es | next segement, test for wraparound at 16M
add _gdt+DS_286_OFFSET+DESC_BASE,#PCM_DENSITY
seg es | assuming es == old ds
adcb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,#0
loopnz pcm_loop
pcm_exit:
pop ax
sub ax,cx | verified size in multiples of PCM_DENSITY
pcm_1exit:
movb dl,ah | convert to phys_bytes in dx:ax
subb dh,dh
movb ah,al
movb al,dh
pop ds
ret
|*===========================================================================*
|* p_cp_mess *
|*===========================================================================*
| The real mode version attempts to be efficient by passing raw segments but
| that just gets in the way here.
p_cp_mess:
pop dx
pop bx | proc
pop cx | source clicks
pop ax | source offset
#if CLICK_SHIFT != HCLICK_SHIFT + 4
#error /* the only click size supported is 256, to avoid slow shifts here */
#endif
addb ah,cl | calculate source offset
adcb ch,#0 | and put in base of source descriptor
mov _gdt+DS_286_OFFSET+DESC_BASE,ax
movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,ch
pop cx | destination clicks
pop ax | destination offset
addb ah,cl | calculate destination offset
adcb ch,#0 | and put in base of destination descriptor
mov _gdt+ES_286_OFFSET+DESC_BASE,ax
movb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE,ch
sub sp,#2+2+2+2+2
push ds
push es
mov ax,#DS_286_SELECTOR
mov ds,ax
mov ax,#ES_286_SELECTOR
mov es,ax
seg es
mov 0,bx | sender's proc no. from arg, not msg
mov ax,si
mov bx,di
mov si,#2 | src offset is now 2 relative to start of seg
mov di,si | and destination offset
mov cx,#Msize-1 | word count
rep
movw
mov di,bx
mov si,ax
pop es
pop ds
jmp (dx)
|*===========================================================================*
|* p_phys_copy *
|*===========================================================================*
p_phys_copy:
pop dx
pop _gdt+DS_286_OFFSET+DESC_BASE
pop ax | pop source into base of source descriptor
movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,al
pop _gdt+ES_286_OFFSET+DESC_BASE
pop ax | pop destination into base of dst descriptor
movb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE,al
pop cx | byte count in bx:cx
pop bx
sub sp,#4+4+4
push di
push si
push es
push ds
sub si,si | src offset is now 0 relative to start of seg
mov di,si | and destination offset
j ppc_next
| It is too much trouble to align the segment bases, so word alignment is hard.
| Avoiding the book-keeping for alignment may be good anyway.
ppc_large:
push cx
mov cx,#0x8000 | copy a large chunk of this many words
rep
movw
pop cx
dec bx
pop ds | update the descriptors
incb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE
incb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE
push ds
ppc_next:
mov ax,#DS_286_SELECTOR | (re)load the selectors
mov ds,ax
mov ax,#ES_286_SELECTOR
mov es,ax
test bx,bx
jnz ppc_large
shr cx,#1 | word count
rep
movw | move any leftover words
rcl cx,#1 | restore old bit 0
rep
movb | move any leftover byte
pop ds
pop es
pop si
pop di
jmp (dx)
|*===========================================================================*
|* p_port_read *
|*===========================================================================*
p_port_read:
pop bx
pop dx | port
pop _gdt+ES_286_OFFSET+DESC_BASE
pop ax | pop destination into base of dst descriptor
movb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE,al
pop cx | byte count
sub sp,#2+4+2
push es
mov ax,#ES_286_SELECTOR
mov es,ax
mov ax,di
sub di,di | dst offset is now 0 relative to start of seg
shr cx,#1 | word count
rep
insw | read everything
mov di,ax
pop es
jmp (bx)
|*===========================================================================*
|* p_port_write *
|*===========================================================================*
p_port_write:
pop bx
pop dx | port
pop _gdt+DS_286_OFFSET+DESC_BASE
pop ax | pop offset into base of source descriptor
movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,al
pop cx | byte count, discard high word
sub sp,#2+4+2
push ds
mov ax,#DS_286_SELECTOR
mov ds,ax
mov ax,si
sub si,si | src offset is now 0 relative to start of seg
shr cx,#1 | word count
rep
outsw | write everything
mov si,ax
pop ds
jmp (bx)
|*===========================================================================*
|* data *
|*===========================================================================*
.data
patch_table: | pairs (old function, new function)
.word _check_mem, p_check_mem
.word _cp_mess, p_cp_mess
.word _phys_copy, p_phys_copy
.word _port_read, p_port_read
.word _port_write, p_port_write
.word _restart, p_restart | in mpx file
.word save, p_save | in mpx file
.word 0 | end of table
splimit: | stack limit for current task (kernel only)
.word 0
vidlock: | dummy variable for use with lock prefix
.word 0