6be8c4d8a3
Giovanni Falzoni <fgalzoni@inwind.it>.
561 lines
14 KiB
ArmAsm
Executable file
561 lines
14 KiB
ArmAsm
Executable file
#
|
|
! sections
|
|
|
|
.sect .text; .sect .rom; .sect .data; .sect .bss
|
|
|
|
#include <minix/config.h>
|
|
#include <minix/const.h>
|
|
#include "const.h"
|
|
#include "sconst.h"
|
|
#include "protect.h"
|
|
|
|
! This file contains a number of assembly code utility routines needed by the
|
|
! kernel. They are:
|
|
|
|
.define _monitor ! exit Minix and return to the monitor
|
|
.define _int86 ! let the monitor make an 8086 interrupt call
|
|
.define _cp_mess ! copies messages from source to destination
|
|
.define _exit ! dummy for library routines
|
|
.define __exit ! dummy for library routines
|
|
.define ___exit ! dummy for library routines
|
|
.define ___main ! dummy for GCC
|
|
.define _phys_insw ! transfer data from (disk controller) port to memory
|
|
.define _phys_insb ! likewise byte by byte
|
|
.define _phys_outsw ! transfer data from memory to (disk controller) port
|
|
.define _phys_outsb ! likewise byte by byte
|
|
.define _enable_irq ! enable an irq at the 8259 controller
|
|
.define _disable_irq ! disable an irq
|
|
.define _phys_copy ! copy data from anywhere to anywhere in memory
|
|
.define _phys_zero ! zero data anywhere in memory
|
|
.define _mem_rdw ! copy one word from [segment:offset]
|
|
.define _reset ! reset the system
|
|
.define _idle_task ! task executed when there is no work
|
|
.define _level0 ! call a function at level 0
|
|
.define _read_tsc ! read the cycle counter (Pentium and up)
|
|
.define _read_cpu_flags ! read the cpu flags
|
|
|
|
! The routines only guarantee to preserve the registers the C compiler
|
|
! expects to be preserved (ebx, esi, edi, ebp, esp, segment registers, and
|
|
! direction bit in the flags).
|
|
|
|
.sect .text
|
|
!*===========================================================================*
|
|
!* monitor *
|
|
!*===========================================================================*
|
|
! PUBLIC void monitor();
|
|
! Return to the monitor.
|
|
|
|
_monitor:
|
|
mov esp, (_mon_sp) ! restore monitor stack pointer
|
|
o16 mov dx, SS_SELECTOR ! monitor data segment
|
|
mov ds, dx
|
|
mov es, dx
|
|
mov fs, dx
|
|
mov gs, dx
|
|
mov ss, dx
|
|
pop edi
|
|
pop esi
|
|
pop ebp
|
|
o16 retf ! return to the monitor
|
|
|
|
|
|
!*===========================================================================*
|
|
!* int86 *
|
|
!*===========================================================================*
|
|
! PUBLIC void int86();
|
|
_int86:
|
|
cmpb (_mon_return), 0 ! is the monitor there?
|
|
jnz 0f
|
|
movb ah, 0x01 ! an int 13 error seems appropriate
|
|
movb (_reg86+ 0), ah ! reg86.w.f = 1 (set carry flag)
|
|
movb (_reg86+13), ah ! reg86.b.ah = 0x01 = "invalid command"
|
|
ret
|
|
0: push ebp ! save C registers
|
|
push esi
|
|
push edi
|
|
push ebx
|
|
pushf ! save flags
|
|
cli ! no interruptions
|
|
|
|
inb INT2_CTLMASK
|
|
movb ah, al
|
|
inb INT_CTLMASK
|
|
push eax ! save interrupt masks
|
|
mov eax, (_irq_use) ! map of in-use IRQ`s
|
|
and eax, ~[1<<CLOCK_IRQ] ! keep the clock ticking
|
|
outb INT_CTLMASK ! enable all unused IRQ`s and vv.
|
|
movb al, ah
|
|
outb INT2_CTLMASK
|
|
|
|
mov eax, SS_SELECTOR ! monitor data segment
|
|
mov ss, ax
|
|
xchg esp, (_mon_sp) ! switch stacks
|
|
push (_reg86+36) ! parameters used in INT call
|
|
push (_reg86+32)
|
|
push (_reg86+28)
|
|
push (_reg86+24)
|
|
push (_reg86+20)
|
|
push (_reg86+16)
|
|
push (_reg86+12)
|
|
push (_reg86+ 8)
|
|
push (_reg86+ 4)
|
|
push (_reg86+ 0)
|
|
mov ds, ax ! remaining data selectors
|
|
mov es, ax
|
|
mov fs, ax
|
|
mov gs, ax
|
|
push cs
|
|
push return ! kernel return address and selector
|
|
o16 jmpf 20+2*4+10*4+2*4(esp) ! make the call
|
|
return:
|
|
pop (_reg86+ 0)
|
|
pop (_reg86+ 4)
|
|
pop (_reg86+ 8)
|
|
pop (_reg86+12)
|
|
pop (_reg86+16)
|
|
pop (_reg86+20)
|
|
pop (_reg86+24)
|
|
pop (_reg86+28)
|
|
pop (_reg86+32)
|
|
pop (_reg86+36)
|
|
lgdt (_gdt+GDT_SELECTOR) ! reload global descriptor table
|
|
jmpf CS_SELECTOR:csinit ! restore everything
|
|
csinit: mov eax, DS_SELECTOR
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov fs, ax
|
|
mov gs, ax
|
|
mov ss, ax
|
|
xchg esp, (_mon_sp) ! unswitch stacks
|
|
lidt (_gdt+IDT_SELECTOR) ! reload interrupt descriptor table
|
|
andb (_gdt+TSS_SELECTOR+DESC_ACCESS), ~0x02 ! clear TSS busy bit
|
|
mov eax, TSS_SELECTOR
|
|
ltr ax ! set TSS register
|
|
|
|
pop eax
|
|
outb INT_CTLMASK ! restore interrupt masks
|
|
movb al, ah
|
|
outb INT2_CTLMASK
|
|
|
|
add (_lost_ticks), ecx ! record lost clock ticks
|
|
|
|
popf ! restore flags
|
|
pop ebx ! restore C registers
|
|
pop edi
|
|
pop esi
|
|
pop ebp
|
|
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 DWORDS (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.
|
|
|
|
CM_ARGS = 4 + 4 + 4 + 4 + 4 ! 4 + 4 + 4 + 4 + 4
|
|
! es ds edi esi eip proc scl sof dcl dof
|
|
|
|
.align 16
|
|
_cp_mess:
|
|
cld
|
|
push esi
|
|
push edi
|
|
push ds
|
|
push es
|
|
|
|
mov eax, FLAT_DS_SELECTOR
|
|
mov ds, ax
|
|
mov es, ax
|
|
|
|
mov esi, CM_ARGS+4(esp) ! src clicks
|
|
shl esi, CLICK_SHIFT
|
|
add esi, CM_ARGS+4+4(esp) ! src offset
|
|
mov edi, CM_ARGS+4+4+4(esp) ! dst clicks
|
|
shl edi, CLICK_SHIFT
|
|
add edi, CM_ARGS+4+4+4+4(esp) ! dst offset
|
|
|
|
mov eax, CM_ARGS(esp) ! process number of sender
|
|
stos ! copy number of sender to dest message
|
|
add esi, 4 ! do not copy first word
|
|
mov ecx, Msize - 1 ! remember, first word does not count
|
|
rep
|
|
movs ! copy the message
|
|
|
|
pop es
|
|
pop ds
|
|
pop edi
|
|
pop esi
|
|
ret ! that is 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.
|
|
! GNU CC likes to call ___main from main() for nonobvious reasons.
|
|
|
|
_exit:
|
|
__exit:
|
|
___exit:
|
|
sti
|
|
jmp ___exit
|
|
|
|
___main:
|
|
ret
|
|
|
|
|
|
!*===========================================================================*
|
|
!* phys_insw *
|
|
!*===========================================================================*
|
|
! PUBLIC void phys_insw(Port_t port, phys_bytes buf, size_t count);
|
|
! Input an array from an I/O port. Absolute address version of insw().
|
|
|
|
_phys_insw:
|
|
push ebp
|
|
mov ebp, esp
|
|
cld
|
|
push edi
|
|
push es
|
|
mov ecx, FLAT_DS_SELECTOR
|
|
mov es, cx
|
|
mov edx, 8(ebp) ! port to read from
|
|
mov edi, 12(ebp) ! destination addr
|
|
mov ecx, 16(ebp) ! byte count
|
|
shr ecx, 1 ! word count
|
|
rep o16 ins ! input many words
|
|
pop es
|
|
pop edi
|
|
pop ebp
|
|
ret
|
|
|
|
|
|
!*===========================================================================*
|
|
!* phys_insb *
|
|
!*===========================================================================*
|
|
! PUBLIC void phys_insb(Port_t port, phys_bytes buf, size_t count);
|
|
! Input an array from an I/O port. Absolute address version of insb().
|
|
|
|
_phys_insb:
|
|
push ebp
|
|
mov ebp, esp
|
|
cld
|
|
push edi
|
|
push es
|
|
mov ecx, FLAT_DS_SELECTOR
|
|
mov es, cx
|
|
mov edx, 8(ebp) ! port to read from
|
|
mov edi, 12(ebp) ! destination addr
|
|
mov ecx, 16(ebp) ! byte count
|
|
! shr ecx, 1 ! word count
|
|
rep insb ! input many bytes
|
|
pop es
|
|
pop edi
|
|
pop ebp
|
|
ret
|
|
|
|
|
|
!*===========================================================================*
|
|
!* phys_outsw *
|
|
!*===========================================================================*
|
|
! PUBLIC void phys_outsw(Port_t port, phys_bytes buf, size_t count);
|
|
! Output an array to an I/O port. Absolute address version of outsw().
|
|
|
|
.align 16
|
|
_phys_outsw:
|
|
push ebp
|
|
mov ebp, esp
|
|
cld
|
|
push esi
|
|
push ds
|
|
mov ecx, FLAT_DS_SELECTOR
|
|
mov ds, cx
|
|
mov edx, 8(ebp) ! port to write to
|
|
mov esi, 12(ebp) ! source addr
|
|
mov ecx, 16(ebp) ! byte count
|
|
shr ecx, 1 ! word count
|
|
rep o16 outs ! output many words
|
|
pop ds
|
|
pop esi
|
|
pop ebp
|
|
ret
|
|
|
|
|
|
!*===========================================================================*
|
|
!* phys_outsb *
|
|
!*===========================================================================*
|
|
! PUBLIC void phys_outsb(Port_t port, phys_bytes buf, size_t count);
|
|
! Output an array to an I/O port. Absolute address version of outsb().
|
|
|
|
.align 16
|
|
_phys_outsb:
|
|
push ebp
|
|
mov ebp, esp
|
|
cld
|
|
push esi
|
|
push ds
|
|
mov ecx, FLAT_DS_SELECTOR
|
|
mov ds, cx
|
|
mov edx, 8(ebp) ! port to write to
|
|
mov esi, 12(ebp) ! source addr
|
|
mov ecx, 16(ebp) ! byte count
|
|
rep outsb ! output many bytes
|
|
pop ds
|
|
pop esi
|
|
pop ebp
|
|
ret
|
|
|
|
|
|
!*==========================================================================*
|
|
!* enable_irq *
|
|
!*==========================================================================*/
|
|
! PUBLIC void enable_irq(irq_hook_t *hook)
|
|
! Enable an interrupt request line by clearing an 8259 bit.
|
|
! Equivalent C code for hook->irq < 8:
|
|
! if ((irq_actids[hook->irq] &= ~hook->id) == 0)
|
|
! outb(INT_CTLMASK, inb(INT_CTLMASK) & ~(1 << irq));
|
|
|
|
.align 16
|
|
_enable_irq:
|
|
push ebp
|
|
mov ebp, esp
|
|
pushf
|
|
cli
|
|
mov eax, 8(ebp) ! hook
|
|
mov ecx, 8(eax) ! irq
|
|
mov eax, 12(eax) ! id bit
|
|
not eax
|
|
and _irq_actids(ecx*4), eax ! clear this id bit
|
|
jnz en_done ! still masked by other handlers?
|
|
movb ah, ~1
|
|
rolb ah, cl ! ah = ~(1 << (irq % 8))
|
|
mov edx, INT_CTLMASK ! enable irq < 8 at the master 8259
|
|
cmpb cl, 8
|
|
jb 0f
|
|
mov edx, INT2_CTLMASK ! enable irq >= 8 at the slave 8259
|
|
0: inb dx
|
|
andb al, ah
|
|
outb dx ! clear bit at the 8259
|
|
en_done:popf
|
|
leave
|
|
ret
|
|
|
|
|
|
!*==========================================================================*
|
|
!* disable_irq *
|
|
!*==========================================================================*/
|
|
! PUBLIC int disable_irq(irq_hook_t *hook)
|
|
! Disable an interrupt request line by setting an 8259 bit.
|
|
! Equivalent C code for irq < 8:
|
|
! irq_actids[hook->irq] |= hook->id;
|
|
! outb(INT_CTLMASK, inb(INT_CTLMASK) | (1 << irq));
|
|
! Returns true iff the interrupt was not already disabled.
|
|
|
|
.align 16
|
|
_disable_irq:
|
|
push ebp
|
|
mov ebp, esp
|
|
pushf
|
|
cli
|
|
mov eax, 8(ebp) ! hook
|
|
mov ecx, 8(eax) ! irq
|
|
mov eax, 12(eax) ! id bit
|
|
or _irq_actids(ecx*4), eax ! set this id bit
|
|
movb ah, 1
|
|
rolb ah, cl ! ah = (1 << (irq % 8))
|
|
mov edx, INT_CTLMASK ! disable irq < 8 at the master 8259
|
|
cmpb cl, 8
|
|
jb 0f
|
|
mov edx, INT2_CTLMASK ! disable irq >= 8 at the slave 8259
|
|
0: inb dx
|
|
testb al, ah
|
|
jnz dis_already ! already disabled?
|
|
orb al, ah
|
|
outb dx ! set bit at the 8259
|
|
mov eax, 1 ! disabled by this function
|
|
popf
|
|
leave
|
|
ret
|
|
dis_already:
|
|
xor eax, eax ! already disabled
|
|
popf
|
|
leave
|
|
ret
|
|
|
|
|
|
!*===========================================================================*
|
|
!* phys_copy *
|
|
!*===========================================================================*
|
|
! PUBLIC void phys_copy(phys_bytes source, phys_bytes destination,
|
|
! phys_bytes bytecount);
|
|
! Copy a block of physical memory.
|
|
|
|
PC_ARGS = 4 + 4 + 4 + 4 ! 4 + 4 + 4
|
|
! es edi esi eip src dst len
|
|
|
|
.align 16
|
|
_phys_copy:
|
|
cld
|
|
push esi
|
|
push edi
|
|
push es
|
|
|
|
mov eax, FLAT_DS_SELECTOR
|
|
mov es, ax
|
|
|
|
mov esi, PC_ARGS(esp)
|
|
mov edi, PC_ARGS+4(esp)
|
|
mov eax, PC_ARGS+4+4(esp)
|
|
|
|
cmp eax, 10 ! avoid align overhead for small counts
|
|
jb pc_small
|
|
mov ecx, esi ! align source, hope target is too
|
|
neg ecx
|
|
and ecx, 3 ! count for alignment
|
|
sub eax, ecx
|
|
rep
|
|
eseg movsb
|
|
mov ecx, eax
|
|
shr ecx, 2 ! count of dwords
|
|
rep
|
|
eseg movs
|
|
and eax, 3
|
|
pc_small:
|
|
xchg ecx, eax ! remainder
|
|
rep
|
|
eseg movsb
|
|
|
|
pop es
|
|
pop edi
|
|
pop esi
|
|
ret
|
|
|
|
!*===========================================================================*
|
|
!* phys_zero *
|
|
!*===========================================================================*
|
|
! PUBLIC void phys_zero(phys_bytes source, phys_bytes bytecount);
|
|
! Zero a block of physical memory.
|
|
|
|
.align 16
|
|
|
|
_phys_zero:
|
|
push ebp
|
|
mov ebp, esp
|
|
push esi
|
|
push ebx
|
|
push ds
|
|
mov esi, 8(ebp)
|
|
mov eax, 12(ebp)
|
|
mov ebx, FLAT_DS_SELECTOR
|
|
mov ds, bx
|
|
shr eax, 2
|
|
zero_start:
|
|
mov (esi), 0
|
|
add esi, 4
|
|
dec eax
|
|
jnz zero_start
|
|
zero_done:
|
|
pop ds
|
|
pop ebx
|
|
pop esi
|
|
pop ebp
|
|
ret
|
|
|
|
!*===========================================================================*
|
|
!* mem_rdw *
|
|
!*===========================================================================*
|
|
! PUBLIC u16_t mem_rdw(U16_t segment, u16_t *offset);
|
|
! Load and return word at far pointer segment:offset.
|
|
|
|
.align 16
|
|
_mem_rdw:
|
|
mov cx, ds
|
|
mov ds, 4(esp) ! segment
|
|
mov eax, 4+4(esp) ! offset
|
|
movzx eax, (eax) ! word to return
|
|
mov ds, cx
|
|
ret
|
|
|
|
|
|
!*===========================================================================*
|
|
!* reset *
|
|
!*===========================================================================*
|
|
! PUBLIC void reset();
|
|
! Reset the system by loading IDT with offset 0 and interrupting.
|
|
|
|
_reset:
|
|
lidt (idt_zero)
|
|
int 3 ! anything goes, the 386 will not like it
|
|
.sect .data
|
|
idt_zero: .data4 0, 0
|
|
.sect .text
|
|
|
|
|
|
!*===========================================================================*
|
|
!* idle_task *
|
|
!*===========================================================================*
|
|
_idle_task:
|
|
! This task is called when the system has nothing else to do. The HLT
|
|
! instruction puts the processor in a state where it draws minimum power.
|
|
push halt
|
|
call _level0 ! level0(halt)
|
|
pop eax
|
|
jmp _idle_task
|
|
halt:
|
|
sti
|
|
hlt
|
|
cli
|
|
ret
|
|
|
|
!*===========================================================================*
|
|
!* level0 *
|
|
!*===========================================================================*
|
|
! PUBLIC void level0(void (*func)(void))
|
|
! Call a function at permission level 0. This allows kernel tasks to do
|
|
! things that are only possible at the most privileged CPU level.
|
|
!
|
|
_level0:
|
|
mov eax, 4(esp)
|
|
mov (_level0_func), eax
|
|
int LEVEL0_VECTOR
|
|
ret
|
|
|
|
|
|
!*===========================================================================*
|
|
!* read_tsc *
|
|
!*===========================================================================*
|
|
! PUBLIC void read_tsc(unsigned long *high, unsigned long *low);
|
|
! Read the cycle counter of the CPU. Pentium and up.
|
|
.align 16
|
|
_read_tsc:
|
|
.data1 0x0f ! this is the RDTSC instruction
|
|
.data1 0x31 ! it places the TSC in EDX:EAX
|
|
push ebp
|
|
mov ebp, 8(esp)
|
|
mov (ebp), edx
|
|
mov ebp, 12(esp)
|
|
mov (ebp), eax
|
|
pop ebp
|
|
ret
|
|
|
|
!*===========================================================================*
|
|
!* read_flags *
|
|
!*===========================================================================*
|
|
! PUBLIC unsigned long read_cpu_flags(void);
|
|
! Read the cycle counter of the CPU. Pentium and up.
|
|
.align 16
|
|
_read_cpu_flags:
|
|
pushf
|
|
mov eax, (esp)
|
|
popf
|
|
ret
|
|
|