Intel 80x86 processors - Assembler etc


8088 - 80286		80386-80486
The 80386 is probably the most significant of the group so this note is written mostly with that processor in mind

The 80386 can operate in "real mode" or in "protected mode"   In real mode 80386 appears to be a very powerful 8088.  More on protected mode later.
80x86 registers and their functions

31                                                          16 | 15                       8 | 7                         0	bit number

|                                                        EAX                                                                 |                                                                                       
				    |                           AX                            |
|                                                                    |              AH           |        AL                |

|                                                        EBX                                                                 |                                                                                       
				    |                           BX                            |
|                                                                    |              BH           |        BL                |

|                                                        ECX                                                                 |                                                                                       
				    |                           CX                            |
|                                                                    |              CH           |        CL                |

|                                                        EDX                                                                  |                                                                                       
				    |                           DX                             |
|                                                                    |              DH           |        DL                 |
	


Register		name		operations
EAX,AX(AH,AL)	accumulator	ASCII adjust for addition/subtraction
				Convert byte to word/word to double word/double word to quad word
				Unsigned multiplication/division I/O fast shifts
				Signed divide
				Load/store flags
				Load/compare/store string operations
				Table look up translations
EBX,BX(BH,BL)	base		pointer to a base address in data segment	
				Table look up translations
ECX,CX(CH,CL)	count		count values for repeats, shifts, rotations
EDX,DX(DH,DL)	data register	word/double word multiplications/divisions 
				Indirect I/O

31                                                          16 | 15                                                       0	bit number

|                                                        ESP                                                                  |                                                                                       
|                                                                    |                             SP                           |
|                                                        EBP                                                                  |                                                                                       
|                                                                    |                             BP                          |
|                                                        ESI                                                                   |                                                                                       
|                                                                    |                             SI                            |
|                                                        EDI                                                                  |                                                                                       
|                                                                    |                             DI                           |

BP		base pointer	pointer to base address in stack segment
SP		stack pointer	stack
SI		source index	source string and index pointer
DI		destination index	destination string and index pointer

BP,SI,DI and SP were specialised in pre-80386 but can now be used as data operands, base addresses and index addresses.  SP cannot be used as an index.  (In the 80386 when used to hold real mode address information the length of the base was always 16bit - how this affects other, 80486 etc, CPU I don't know)


CS		code segment	used with IP, the instruction pointer to find the next m/c code instruction at CS:IP
DS		data segment
SS		stack segment	stack  Used with SP  as SS:SP
ES		extra segment
FS		extra segment	(not present in 8088-80286)
GS		extra segment	(not present in 8088-80286)
IP		instruction pointer	instruction offset
Flags
	1.  Carry flag CF or C.   Set if an operation needs a carry or a borrow.  Otherwise reset (set to 0)
	2.  Parity flag PF or P.  Set if last operation contains an even number of bits set at 1.  Otherwise reset
	3.  Auxiliary carry flag AF   Set if carry or borrow occurs in high nibble low nibble (more below)
	4.  Zero flag ZF or Z   Set if an operation finishes at 0 otherwise reset.
	5.  Sign flag SF.	Copy of Most Significant Byte of a result.
	6.  Overflow flag OF  When set indicates that the signed operation is out of range. Else reset.
	7.  Interrupt enable IF   If set then CPU recognises maskable interrupts
	8.  Direction DF or D  If set then auto decrementation takes place otherwise auto incrementation (for some instructions only)
	9.  Trap TF 	If set the CPU only does one instruction at a time.  Used in debugging



Instruction Examples
AX	The accumulator 

	OUT 0x70, al	;output the value in al to port 70hex


BX	Used for temporary storage  Used as a pointer to a data object e.g.

	MOV ax,[bx]	; load acc with the value at address bx     It is then possible to increment bx to the next address

CX	Usually holds the repetition for loops

	MOV cx, 0x10	; load cx with 16 decimal = 0x10
	start:		; label
	OUT 0x70,al	; output the value in al through port 0x70
	LOOP start	; decrement cx and go back to start

DX	Mainly used for temporary storage

	MUL bx		; multiply the contents of bx with the contents of ax.  The result is held in dx (high order) AND ax (low order)

BP	Used like bx,cx and dx.  Often bp serves as a pointer to the base of a stack frame.  ss is assumed as the assigned segment register i.e. SS:BP is used for 	memory access.  

	PUSH arg1	; arg1 onto the stack			
	PUSH arg2	; and arg2						
	PUSH arg3	; and arg 3				
	CALL sum							
	etc							
	etc							

						Stack looks like this
						arg1
					bp+8 ->
						arg2
					bp+6 ->
						arg3
					bp+4 ->
						IP
					bp+2 ->
						and BP gets put in here by the instruction ** below -
					bp=sp ->

sum	PROC NEAR	; this is an assembler directive
			; as it is declared NEAR only 2 bytes are
			; needed for the old IP (or, if you like, the return
			; address)
	PUSH  bp								**
	MOV  bp,sp	; load stack into base pointer, sp
	MOV  ax,[bp + 4]	; load acc with arg3		NB ss is assumed here so the address used is ss:bp+4 
	ADD  ax,[bp + 6]  ; add arg2
	ADD  ax,[bp + 8]  ; add arg3
	POP bp		; restore bp			NB ss is assumed here so the address used is ss:sp
	RET		; return
	ENDP		; other part of assembler instruction

SI and DI	These registers can, of course, be used as temporary storage but the use they are "designed for" is shown in the example.  SI points to the source of data to be moved.  DI points to the destination of that data

string	DB  20 DUP ('a@b@c@d@e@f@g@h@i@j@')	; another assembler directive (NB the @ should be ASCII code 1 I 									cant reproduce that with this word processor)
	
	MOV  ax,@data	; load string segment into ax
	MOV  ds,ax
	MOV  ax,0xb800  	; load segment of monochrome video RAM into ax
	CLD		; clear direction flag
	MOV  cx, 0xa	; transfer 10 words of 2 bytes each (character + attribute)
	MOV  si, OFFSET string	; OFFSET is another assembler directive.  The address "string"  is split into segment + offset 												(see below)
	MOV  di, 0x00	; output string in upper left corner of screen
	REP    MOVSW	; ten transfers of one word each

REP MOVSW increases DI and SI by two after every transferred word.  CLD resets the DF flag.  If it is set then SI and DI would be decreased by two.  STD sets the DF flag



	
CS	usually points to the segment of the program being run.  This can be changed if a FAR call is used
DS           ditto but for data
SS	ditto but for the stack
ES FS GS    	more segment registers

ADDRESSING in 80x86

The address of any byte or word or whatever is split into two parts, a SEGMENT and an OFFSET.  Needless to say the reason is historical (hysterical!).  A segment consists of 64Kb (=65536bytes = 0x10000 bytes)of memory.  The offset is the address of a particular memory location WITHIN THAT SEGMENT.   Naturally it should never be greater than or equal to 65536.  An address is calculated from

	address=0x10 * segment + offset			( address=16*segment + offset    in decimal) 

The ONLY real reason for keeping this is backward compatibility.

Examples
	1F36:0A5D  = 1F360 + 0A5D= 1FDBD 	 (=130 493 decimal)
	1FB1:02AD = 1FB10 + 02AD = 1FDBD 

Notice how the same address can be expressed in different segment:offset combinations.



Example:  CS contains 80B8 and the IP contains 019D.  Thus the next instruction is collected from
	80B8:019D = 80B80 + 019D = 80D1D

A physical address can have many logical addresses e.g.
		0x002b:0x0013 	is 0x002B0 + 0x0013 = 0x02C3
  		0x002C:0x0003	is 0x002C0 + 0x0003 = 0x02C3	which is just the same memory location

Stack:-
	The stack address is at SS:SP in real mode.  The stack bottom is at SS:0xFFFE  the top is at SS:SP and it can grow no bigger than to SS:0x00 (actually these addresss can be changed at will).  Each time a word or double word is PUSHed onto the stack SP is decremented by two or four and the lowest part of the word/double word is copied onto the stack

.Type of reference		Segment used	Register Used	Default Selection Rule

Instructions			Code segment	CS register	Automatic with instruction fetch
Stack				Stack segment      ESS register           All stack PUSHes and POPs Any memory reference that 								uses ESP or EBP as base register
Local data			Data segment       DS register	All data references except when relative to the stack or 								string destination
Destination strings		E-Space segment  ES register	Destination of string instructions

.                                                                                                                                                                                                                      .


				
EXTENDED EXAMPLE
The example below is the Linux loading code.  I have left the original code with its comments (preceeded by a !) and added some of my own to help you see how the nemonics work.  My comments are preceeded by a  !!


!
!	setup.S		Copyright (C) 1991, 1992 Linus Torvalds
!
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.
!
!  ************************* the comments below detail how the code has developed DAD
!
! Move PS/2 aux init code to psaux.c
! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
!
! some changes and additional features by Christoph Niemann,
! March 1993/June 1994 (Christoph.Niemann@linux.org)
!
! add APM BIOS checking by Stephen Rothwell, May 1994
! (Stephen.Rothwell@canb.auug.org.au)
!
! High load stuff, initrd support and position independency
! by Hans Lermen & Werner Almesberger, February 1996
! <lermen@elserv.ffm.fgan.de>, <almesber@lrc.epfl.ch>
!
! Video handling moved to video.S by Martin Mares, March 1996
! <mj@k332.feld.cvut.cz>
!
! Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
! parsons) to avoid loadlin confusion, July 1997
!

#define __ASSEMBLY__
#include <linux/config.h>
#include <asm/segment.h>
#include <linux/version.h>		!! These are other files to be included with this one
#include <linux/compile.h>		!! Notice the similarity with C
#include <asm/boot.h>

! Signature words to ensure LILO loaded us right
#define SIG1	0xAA55		!! every time SIG1 appears replace it with 0xAA55 - an assembler directive
#define SIG2	0x5A5A		!! and another

INITSEG  = DEF_INITSEG	! 0x9000, we move boot here - out of the way
SYSSEG   = DEF_SYSSEG	! 0x1000, system loaded at 0x10000 (65536).
SETUPSEG = DEF_SETUPSEG	! 0x9020, this is the current segment
			! ... and the former contents of CS
DELTA_INITSEG = SETUPSEG - INITSEG ! 0x0020

.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text

entry start
start:					!! a label
	jmp	start_of_setup	!! jump (unconditional) to start_of_setup
! ------------------------ start of header --------------------------------
!
! SETUP-header, must start at CS:2 (old 0x9020:2)
!
		.ascii	"HdrS"		! Signature for SETUP-header
		.word	0x0201		! Version number of header format
					! (must be >= 0x0105
					! else old loadlin-1.5 will fail)
realmode_swtch:	.word	0,0		! default_switch,SETUPSEG
start_sys_seg:	.word	SYSSEG
		.word	kernel_version	! pointing to kernel version string
  ! note: above part of header is compatible with loadlin-1.5 (header v1.5),
  !        must not change it

type_of_loader:	.byte	0		! = 0, old one (LILO, Loadlin,
					!      Bootlin, SYSLX, bootsect...)
					! else it is set by the loader:
					! 0xTV: T=0 for LILO
					!	T=1 for Loadlin
					!	T=2 for bootsect-loader
					!	T=3 for SYSLX
					!	T=4 for ETHERBOOT
					!       V = version
loadflags:			! flags, unused bits must be zero (RFU)
LOADED_HIGH	= 1		! bit within loadflags,
				! if set, then the kernel is loaded high
CAN_USE_HEAP	= 0x80		! if set, the loader also has set heap_end_ptr
				! to tell how much space behind setup.S
				| can be used for heap purposes.
				! Only the loader knows what is free!
#ifndef __BIG_KERNEL__
		.byte	0x00
#else
		.byte	LOADED_HIGH
#endif

setup_move_size: .word  0x8000	! size to move, when we (setup) are not
				! loaded at 0x90000. We will move ourselves
				! to 0x90000 then just before jumping into
				! the kernel. However, only the loader
				! know how much of data behind us also needs
				! to be loaded.
code32_start:				! here loaders can put a different
					! start address for 32-bit code.
#ifndef __BIG_KERNEL__
		.long	0x1000		!   0x1000 = default for zImage
#else
		.long	0x100000	! 0x100000 = default for big kernel
#endif
ramdisk_image:	.long	0	! address of loaded ramdisk image
				! Here the loader (or kernel generator) puts
				! the 32-bit address were it loaded the image.
				! This only will be interpreted by the kernel.
ramdisk_size:	.long	0	! its size in bytes
bootsect_kludge:
		.word   bootsect_helper,SETUPSEG
heap_end_ptr:	.word	modelist+1024	! space from here (exclusive) down to
				! end of setup code can be used by setup
				! for local heap purposes.
! ------------------------ end of header ----------------------------------

start_of_setup:
! Bootlin depends on this being done early
	mov	ax,#0x01500		!! load ax (bits 15-0) with 0x01500 
	mov	dl,#0x81		!! this is hard disc drive 1 (0=first 1=second etc)
	int	0x13			!! This uses the BIOS 0x13 with ah=0x15 and dl=81 to determine the type
					!! of HDD.  According to literature ah=3 fixed disc is present.  If 					!! ah=0 no HDD is present @

#ifdef SAFE_RESET_DISK_CONTROLLER	!! if SAFE_RESET_DISK_CONTROLLER is defined then the code down to 					!! #endif is assembled - otherwise it is skipped
! Reset the disk controller.	!! bits 31-0 are called eax, 15-0 are called ax, 15-8 are called ah 
					!! 7-0 are called al
	mov	ax,#0x0000		!! 0 is copied to ax so ah=al=0.  The rest of eax (bits 31-16) are not 					!! touched and can be anything 
	mov	dl,#0x80		!! 0x80 (80hexadecimal) is copied into dl (bits 7-0 of dx)
	int	0x13			!! same sort of routine as above but to reset/initialise HardDiscDrive 					!! 0 (first HDD)
#endif

! set DS=CS, we know that SETUPSEG == CS at this point
	mov	ax,cs		! aka #SETUPSEG
	mov	ds,ax		!! notice how ds can't be loaded directly

! Check signature at end of setup
	cmp	setup_sig1,#SIG1		!! compares the value setup_sigl with the value(immediate) SIG1
	jne	bad_sig
	cmp	setup_sig2,#SIG2
	jne	bad_sig
	jmp	good_sig1

! Routine to print ASCIIz (!! ie ASCII string terminated by a 0) string at DS:SI

prtstr:	lodsb		!! copy a byte (note b at end of lodsb) using si pointer. si is then 
				!! incremented or decremented depending on the DF flag
	and	al,al		!! normal bitwise AND
	jz	fin		!! jump if zero flag =1
	call	prtchr		!! call saving return the routine prtchr
	jmp	prtstr		!! jump to prtstr
	fin:	ret		!! fin is just a label

! Space printing

prtsp2:	call	prtspc		! Print double space
prtspc:	mov	al,#0x20	! Print single space (fall-thru!)

! Part of above routine, this one just prints ASCII al

prtchr:	push	ax
	push	cx
	xor	bh,bh			!! equivalent to making bh=0 which otherwise would need a MOV from 					!! (say) al
	mov	cx,#0x01		!! copy 1 into cx 
	mov	ah,#0x0e		!! copy 0x0e into ah
	int	0x10			!! interrupt 0x10 deals with BIOS video functions.  As ah=0x0e this 					!! will write the code held in al to the video display
	pop	cx
	pop	ax
	ret

beep:	mov	al,#0x07		!! copy 0x07 to al 
	jmp	prtchr			!! and jump back to prtchr which "writes" it on the video display
					!! in fact ASCII 07 causes a beep sound
	
no_sig_mess:	.ascii	"No setup signature found ..."
		db	0x00		!! assembler directive to "define byte" value 0x00

good_sig1:
	jmp	good_sig		

! We now have to find the rest of the setup code/data
bad_sig:
	mov	ax,cs		! aka #SETUPSEG   	!! note from programmer "cs contains the memory address 							!! SETUPSEG" 
	sub	ax,#DELTA_INITSEG ! aka #INITSEG	!! This does the subtraction SETUPSEG - INITSEG
	mov	ds,ax					!! and into ds (usually data)
	xor	bh,bh					!! puts 0 into bh.  
	mov	bl,[497]	! get setup sects from boot sector	   !! load bl from the contents of 0x497
	sub	bx,#4		! LILO loads 4 sectors of setup	   !! subtracts 4 from bh,bl== 0,[497]
	shl	bx,#8		! convert to words	!! shift logical left by 8  This has the effect of 							!! multiplying bx by 256.  Rotations are carried out 							!! through the carry flag CF and may/may not be set
	mov	cx,bx
	shr	bx,#3		! convert to segment	!! shift right this time == divide by 8
	add	bx,#SYSSEG				!! and add SYSSEG
	seg cs						!! change to segment cs.  This normally works with IP to 							!! get the next instruction. 
							!! I dont know why this is done here other 							!! than the possibility of overriding the data segment 
							!! Thus the next instruction could be
							!! 	mov cs:start_sys_seg,bx
							!! other than the default
							!! 	mov ds:start_sys_seg,bx

	mov	start_sys_seg,bx			!!    bx gets copied to the address ds:start_sys_seg
							!! OR bx gets copied to the address cs:start_sys_seg

! Move rest of setup code/data to here
	mov	di,#2048	! four sectors loaded by LILO
	sub	si,si					!! to zero si but also to change flags 
							!! OF=0; ZF=1; SF=0; and AF,PF,CF are affected
	mov	ax,cs		! aka #SETUPSEG
	mov	es,ax				!! this is to copy cs into es - 80386 needs this.
	mov	ax,#SYSSEG
	mov	ds,ax				!! to load ds with SYSSEG
	rep					!! top of the repeat loop involving, in this case, movsw
	movsw					!! bulk movment..  MOVeStringWords.  Source pointed to by si, 						!! destination pointed to by di.  Controlled by cx

	mov	ax,cs		! aka #SETUPSEG
	mov	ds,ax				!! that is copy SETUPSEG into ds
	cmp	setup_sig1,#SIG1		!! this is a good example of how the assembler works
						!! here setup_sig1 is replaced by an address but the assembler 						!! substitutes 0xAA55 for #SIG1 and 0xAA55 is a number NB NOT 						!! AN ADDRESS
						!! cmp affects OF,SF,ZF,AF,PF AND CF.  It does not affect 						!! anything else
	jne	no_sig				!! jump not equal, that is, jump if ZF=0
	cmp	setup_sig2,#SIG2
	jne	no_sig
	jmp	good_sig

no_sig:
	lea	si,no_sig_mess		!! Load Effective Address of "no_sig_mess" into si
	call	prtstr				!! Call the routine "prtstr".  IP and perhaps CS are saved on 						!! the stack for a return
no_sig_loop:
	jmp	no_sig_loop

good_sig:
	mov	ax,cs		! aka #SETUPSEG
	sub	ax,#DELTA_INITSEG ! aka #INITSEG
	mov	ds,ax

! check if an old loader tries to load a big-kernel
	seg cs
	test	byte ptr loadflags,#LOADED_HIGH ! Have we a big kernel?
		!! byte ptr tells the assembler to compare bytes  (word ptr would alter this to words)
		!! test works by doing (loadflags AND LOADED_HIGH)  It does not change either loadflags or 		!! LOADED_HIGH  It does affect the OF=0; SF ZF PF CF flags are altered and the AF flag 		!! undefined
	jz	loader_ok	! NO, no danger even for old loaders
				! YES, we have a big-kernel
	seg cs					!! overrides ds in next instruction (probably)
	cmp	byte ptr type_of_loader,#0 ! Have we one of the new loaders?
	jnz	loader_ok	! YES, OK
				! NO, we have an old loader, must give up
	push    cs
	pop	ds				!! one way of copying cs into ds
	lea	si,loader_panic_mess		!! programmer has made si point to message to be printed
	call	prtstr
	jmp	no_sig_loop
loader_panic_mess: 
	.ascii	"Wrong loader:  giving up."
	db	0				!! ASCIIz string.  This is the 0

loader_ok:
! Get memory size (extended mem, kB)

#ifndef STANDARD_MEMORY_BIOS_CALL
	push	ebx		!! extended bx

        xor     ebx,ebx		! preload new memory slot with 0k
        mov	[0x1e0], ebx		!! copy ebx to memory at 0x1e0 ie DS:0x1e0

        mov     ax,#0xe801
	int     0x15			!! Deals with system device functions but I don't know what happens in 					!! this case but it looks like a check of the memory hardware
	jc      oldstylemem		

! Memory size is in 1 k chunksizes, to avoid confusing loadlin.
! We store the 0xe801 memory size in a completely different place,
! because it will most likely be longer than 16 bits.
! (use 1e0 because that's what Larry Augustine uses in his
! alternative new memory detection scheme, and it's sensible
! to write everything into the same place.)

	and     ebx, #0xffff    ! clear sign extend		!! sets bits 31-16 to 0
	shl     ebx, 6          ! and go from 64k to 1k chunks	!! equivalent to multiply by 64
	mov     [0x1e0],ebx     ! store extended memory size

	and     eax, #0xffff    ! clear sign extend
 	add     [0x1e0],eax     ! and add lower memory into total size.
  
	! and fall into the old memory detection code to populate the
	! compatibility slot.

oldstylemem:
	pop	ebx
#else
	mov     dword ptr [0x1e0], #0	!! double word pointer.  So memory locations 0x1e0=0 and 0x1e2=0
#endif
	mov	ah,#0x88	!! part of int 0x15 call
	int	0x15		!! equivalent to "read extended memory size" This leaves the number of 				!! contiguous 1K blocks in ax.  The starting address is 1024K=0x100000
				!! (This is held in CMOS 0x30 and 0x31 and int0x15 effectively reads the 				!! value)
	mov	[2],ax

! Set the keyboard repeat rate to the max
!! Before call ah=03 al=05 bh=delay value bl=typematic rate
!!			e.g.  bh=02 so delay is 750 milliseconds
!!						e.g. bl=09 so rate is 13.3 characters per second
	mov	ax,#0x0305	
	xor	bx,bx		! clear bx 		!!== 250ms and 30 chars/sec
	int	0x16		 

! Check for video adapter and its parameters and allow the
! user to browse video modes.

	call	video	! NOTE: we need DS pointing to boot sector

! Get hd0 data		!! first HDD

	xor	ax,ax		! clear ax
	mov	ds,ax		!! ds as well
	lds	si,[4*0x41]	!! Load pointer using DS.  The [4*0x41] is called "scaling an index" and the 				!! physical address is worked out using 0x10*DS + 4*0x41
				!! LDS transfers 4 bytes to two registers one of which is ds (in this case the 				!! other is, of course, si. 
	mov	ax,cs		! aka #SETUPSEG
	sub	ax,#DELTA_INITSEG ! aka #INITSEG
	push	ax
	mov	es,ax
	mov	di,#0x0080
	mov	cx,#0x10
	push	cx		!!    **
	cld			!! the DF (also called D) flag is zeroed
	rep			!! repeat
	movsb			!! move from si-> to di-> until cx=0 see up at **

! Get hd1 data	!! second HDD

	xor	ax,ax		! clear ax
	mov	ds,ax
	lds	si,[4*0x46]
	pop	cx
	pop	es
	mov	di,#0x0090
	rep
	movsb

! Check that there IS a hd1 :-)

	mov	ax,#0x01500
	mov	dl,#0x81		!! 0x81 is code used for second HDD  in int0x13
	int	0x13
	jc	no_disk1		!! int 0x13 sets CF=1 if there is an error
	cmp	ah,#3			!! int 0x13 code is put into ah. 00==no drive;01==floppy without 					!! connection for disk change; 02== floppy with connection for disk 					!! change; 03 == HDD
	je	is_disk1		!! or jz
no_disk1:
	mov	ax,cs		! aka #SETUPSEG
	sub	ax,#DELTA_INITSEG ! aka #INITSEG
	mov	es,ax			!! es can't be loaded directly
	mov	di,#0x0090
	mov	cx,#0x10
	xor	ax,ax		! clear ax
	cld				!! DF=0
	rep				!! repeat 
	stosb				!! "store string" b==byte operation
					!! Transfers al contents to destination pointed to by di. If DF=0 di is 					!! incremented else if DF=1 di is decremented
					!! repeat ends when cx=0
is_disk1:

! check for Micro Channel (MCA) bus
	mov ax,cs		! aka #SETUPSEG
	sub ax,#DELTA_INITSEG	! aka #INITSEG
	mov ds,ax
	mov ds,ax
	xor ax,ax
	mov [0xa0], ax		! set table length to 0
	mov ah, #0xc0			!! code for "system configuration parameters" call
	stc				!! CF=1
	int 0x15		! puts feature table at es:bx
	jc no_mca
	push ds
	mov ax,es
	mov ds,ax
	mov ax,cs		! aka #SETUPSEG
	sub ax, #DELTA_INITSEG	! aka #INITSEG
	mov es,ax
	mov si,bx
	mov di,#0xa0
	mov cx,(si)			!! the data in memory pointed to by si, not si itself
	add cx,#2		! table length is a short
	cmp cx,#0x10
	jc sysdesc_ok
	mov cx,#0x10		! we keep only first 16 bytes
sysdesc_ok:
	rep
	movsb
	pop ds

no_mca:

! Check for PS/2 pointing device		!! eg a mouse

	mov	ax,cs		! aka #SETUPSEG
	sub	ax,#DELTA_INITSEG ! aka #INITSEG
	mov	ds,ax			!! next instruction is ds:0x1ff 
	mov	[0x1ff],#0	! default is no pointing device
	int	0x11		! int 0x11: equipment determination
	test	al,#0x04	! check if pointing device installed
	jz	no_psmouse
	mov	[0x1ff],#0xaa	! device present
no_psmouse:

#ifdef CONFIG_APM
! check for APM BIOS
		! NOTE:	DS is pointing to the boot sector
		!
	mov	[64],#0		! version == 0 means no APM BIOS

	mov	ax,#0x05300	! APM BIOS installation check
	xor	bx,bx
	int	0x15		!!  sorry I have no info on this call
	jc	done_apm_bios	! error -> no APM BIOS

	cmp	bx,#0x0504d	! check for "PM" signature
	jne	done_apm_bios	! no signature -> no APM BIOS

	and	cx,#0x02	! Is 32 bit supported?
	je	done_apm_bios	!	no ...

	mov	ax,#0x05304	! Disconnect first just in case
	xor	bx,bx
	int	0x15		! ignore return code

	mov	ax,#0x05303	! 32 bit connect
	xor	bx,bx
	int	0x15
	jc	no_32_apm_bios	! error

	mov	[66],ax		! BIOS code segment
	mov	[68],ebx	! BIOS entry point offset
	mov	[72],cx		! BIOS 16 bit code segment
	mov	[74],dx		! BIOS data segment
	mov	[78],esi	! BIOS code segment length
	mov	[82],di		! BIOS data segment length
!
! Redo the installation check as the 32 bit connect
! modifies the flags returned on some BIOSs
!
	mov	ax,#0x05300	! APM BIOS installation check
	xor	bx,bx
	int	0x15
	jc	apm_disconnect	! error -> should not happen, tidy up

	cmp	bx,#0x0504d	! check for "PM" signature
	jne	apm_disconnect	! no signature -> should not happen, tidy up

	mov	[64],ax		! record the APM BIOS version
	mov	[76],cx		!	and flags
	jmp	done_apm_bios

apm_disconnect:
	mov	ax,#0x05304	! Disconnect
	xor	bx,bx
	int	0x15		! ignore return code
	jmp	done_apm_bios

no_32_apm_bios:
	and	[76], #0xfffd	! remove 32 bit support bit

done_apm_bios:
#endif
!! The programming so far has happened in "real mode"  But protected mode is far better so...
!! Protected mode brings more registers into play
/*******************************
Protected mode is a multitasking mode - ideal for things like UNIX. The computer executes tasks switching from task to task according to some "fair" schedule set up by the operating system.  Each task is allocated some private memory and can access more "global" memory which is shared by all tasks in the system.  This global memory can be used by any task in the system when its turn comes so anything that a task needs to keep has to be held in its private "local" memory.  (NB that the HDD is considered to be part of the memory.)

GDTR (global descriptor table register) is a 47-0 bit register with bits 47-16 pointing to a table and bits 15-0 the length of that table.  The table (Global Descriptor Table) holds information about how the memory of the computer is used.  It holds info about global and local memory.  Each task is allowed access to this table and to its own LDTR (local descriptor table register) 

Interrupts have their own IDTR (interrupt descriptor table register) and is similar to the GDTR.

On power-up the computer is in "real mode" as so before multitasking is possible it has to load the IDTR, GDTR before switching into "protected mode"

Other registers are activated 

	31                                                          16 | 15                       8 | 7                         0	bit number

CR3	|                                             Page Directory page register PDBR                             |                                                                                       

CR2	|                                                  Page Fault linear Address                                        |                                                                                       

CR1	|                                                        Reserved                                                           |                                                                                       

CR0	|pg                                                                                                   |r |ts |em |mp |pe |                                                                                       
				    
	
The bits PE,MP,EM,TS,R and PG are:-
PE = 0 at reset.  If it changes to 1 the CPU switches into the protected mode
MP = 1 if a numeric co-processor is present
EM = 1 if software emulation of the co-processor is to be used
R  = 1 80387 co-processor 	= 0 80287 co-processor
TS is set when the 80386 switches from one task to another.  It can be reset by software
PG = 1 means that "paged memory" is to be used
PE,MP,EM,TS,R are collectively known as the "Machine Status Word Register", MSWR
********************************/

! Now we want to move to protected mode ...

	seg cs
	cmp	realmode_swtch,#0		!! the value at realmode_swtch (see below) is compared with 0
	jz	rmodeswtch_normal
	seg cs
	callf	far * realmode_swtch		!! far * realmode_swtch is a C style far pointer. 
						!! callf (other assemblers use a slightly different syntax) 						!! makes the assembler use the full address pointed to by 						!! realmode_swtch.  Thus a five byte instruction is generated  						!! Otherwise the assembler will only generate a 3 byte 						!!instruction which relies on the present value of cs
	jmp	rmodeswtch_end
rmodeswtch_normal:
        push	cs
	call	default_switch
rmodeswtch_end:

! we get the code32 start address and modify the below 'jmpi'
! (loader may have changed it)
	seg cs				!! we want to use code32_start which is in the code segment otherwise 					!! the assembler will assume ds			
	mov	eax,code32_start
	seg cs
	mov	code32,eax

! Now we move the system to its rightful place
! ...but we check, if we have a big-kernel.
! in this case we *must* not move it ...
	seg cs
	test	byte ptr loadflags,#LOADED_HIGH	!! byte ptr tells the assembler that the test is bite 							!! sized.  test only affects the flags
	jz	do_move0	! we have a normal low loaded zImage
				! we have a high loaded big kernel
	jmp	end_move	! ... and we skip moving

do_move0:
	mov	ax,#0x100	! start of destination segment
	mov	bp,cs		! aka #SETUPSEG
	sub	bp,#DELTA_INITSEG ! aka #INITSEG
	seg cs
	mov	bx,start_sys_seg	! start of source segment
	cld			! 'direction'=0, movs moves forward	!! DF=0 mentioned above
do_move:
	mov	es,ax		! destination segment
	inc	ah		! instead of add ax,#0x100
	mov	ds,bx		! source segment
	add	bx,#0x100
	sub	di,di
	sub	si,si
	mov 	cx,#0x800
	rep			!! repeat
	movsw			!! move (word sized) using si (source) and di (destination) and cx(counter)
	cmp	bx,bp		! we assume start_sys_seg > 0x200,
				! so we will perhaps read one page more than
				! needed, but never overwrite INITSEG because
				! destination is minimum one page below source
	jb	do_move

! then we load the segment descriptors 	!! Talking about GDTR etc (see above)

end_move:
	mov	ax,cs ! aka #SETUPSEG	! right, forgot this at first. didn't work :-)
	mov	ds,ax

! If we have our code not at 0x90000, we need to move it there now.
! We also then need to move the parameters behind it (command line)
! Because we would overwrite the code on the current IP, we move
! it in two steps, jumping high after the first one.
	mov	ax,cs
	cmp	ax,#SETUPSEG
	je	end_move_self
	cli	! make sure we really have interrupts disabled !	!! CLear Interrupt enable resets IF
		! because after this the stack should not be used
	sub	ax,#DELTA_INITSEG ! aka #INITSEG
	mov	dx,ss
	cmp	dx,ax
	jb	move_self_1		!! CF=1 for the jump
	add	dx,#INITSEG
	sub	dx,ax		! this will be SS after the move
move_self_1:
	mov	ds,ax
	mov	ax,#INITSEG	! real INITSEG
	mov	es,ax
	seg cs
	mov	cx,setup_move_size
	std		! we have to move up, so we use direction down  !! makes DF=1 so si and di decrement
			! because the areas may overlap
	mov	di,cx
	dec	di
	mov	si,di
	sub	cx,#move_self_here+0x200
	rep			!! repeat
	movsb			!! MOVe String - Moving a byte a time
	jmpi	move_self_here,SETUPSEG ! jump to our final place	!! I have no idea ????
move_self_here:
	mov	cx,#move_self_here+0x200
	rep
	movsb
	mov	ax,#SETUPSEG
	mov	ds,ax
	mov	ss,dx
			! now we are at the right place
end_move_self:

	lidt	idt_48		! load idt with 0,0				!! interrupt descriptor table
	lgdt	gdt_48		! load gdt with whatever appropriate  	!! global descriptor table

! that was painless, now we enable A20	!! address line 20 - a bug hangover from previous processors 						!! using DOS. Linux does not have this bug

	call	empty_8042
	mov	al,#0xD1		! command write
	out	#0x64,al			!!  Output al to port 0x64.  Port 0x64 is a command port
	call	empty_8042
	mov	al,#0xDF		! A20 on
	out	#0x60,al			!! Probably the same port
	call	empty_8042

! wait until a20 really *is* enabled; it can take a fair amount of
! time on certain systems; Toshiba Tecras are known to have this
! problem.  The memory location used here is the int 0x1f vector,
! which should be safe to use; any *unused* memory location < 0xfff0
! should work here.  

#define	TEST_ADDR 0x7c

	push	ds
	xor	ax,ax			! segment 0x0000
	mov	ds,ax
	dec	ax			! segment 0xffff (HMA)
	mov	gs,ax
	mov	bx,[TEST_ADDR]		! we want to restore the value later
a20_wait:
	inc	ax
	mov	[TEST_ADDR],ax
	seg	gs
	cmp	ax,[TEST_ADDR+0x10]
	je	a20_wait		! loop until no longer aliased
	mov	[TEST_ADDR],bx		! restore original value
	pop	ds
		
! make sure any possible coprocessor is properly reset..

	xor	ax,ax
	out	#0xf0,al		!! co-processor port 0xf0 - 0xff
	call	delay
	out	#0xf1,al
	call	delay

! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
! we put them right after the intel-reserved hardware interrupts, at
! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
! messed this up with the original PC, and they haven't been able to
! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
! which is used for the internal hardware interrupts as well. We just
! have to reprogram the 8259's, and it isn't fun.

	mov	al,#0x11		! initialization sequence
	out	#0x20,al		! send it to 8259A-1
	call	delay
	out	#0xA0,al		! and to 8259A-2
	call	delay
	mov	al,#0x20		! start of hardware int's (0x20)
	out	#0x21,al
	call	delay
	mov	al,#0x28		! start of hardware int's 2 (0x28)
	out	#0xA1,al
	call	delay
	mov	al,#0x04		! 8259-1 is master
	out	#0x21,al
	call	delay
	mov	al,#0x02		! 8259-2 is slave
	out	#0xA1,al
	call	delay
	mov	al,#0x01		! 8086 mode for both
	out	#0x21,al
	call	delay
	out	#0xA1,al
	call	delay
	mov	al,#0xFF		! mask off all interrupts for now
	out	#0xA1,al
	call	delay
	mov	al,#0xFB		! mask all irq's but irq2 which
	out	#0x21,al		! is cascaded

! Well, that certainly wasn't fun :-(. Hopefully it works, and we don't
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS routine wants lots of unnecessary data, and it's less
! "interesting" anyway. This is how REAL programmers do it.
!
! Well, now's the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the GNU-compiled 32-bit programs do that. We just jump to
! absolute address 0x1000 (or the loader supplied one),
! in 32-bit protected mode.
!
! Note that the short jump isn't strictly needed, although there are
! reasons why it might be a good idea. It won't hurt in any case.
!
	mov	ax,#1		! protected mode (PE) bit	!! bit 0 in CR0
	lmsw	ax		! This is it!			!! "load machine status word register" ie CR0 								!! bits 4-0 (see above)
			!! NOW IN PROTECTED MODE
	jmp	flush_instr_instr:		!! This looks odd to me
	xor	bx,bx		! Flag to indicate a boot

! NOTE: For high loaded big kernels we need a
!	jmpi    0x100000,__KERNEL_CS
!
!	but we yet haven't reloaded the CS register, so the default size 
!	of the target offset still is 16 bit.
!       However, using an operant prefix (0x66), the CPU will properly
!	take our 48 bit far pointer. (INTeL 80386 Programmer's Reference
!	Manual, Mixing 16-bit and 32-bit code, page 16-6)
	!! This hack suggests that jmpi is a "special" - I cant find it in 80386 books
	db	0x66,0xea	! prefix + jmpi-opcode
code32:	dd	0x1000		! will be set to 0x100000 for big kernels
	dw	__KERNEL_CS


kernel_version:	.ascii	UTS_RELEASE
		.ascii	" ("
		.ascii	LINUX_COMPILE_BY
		.ascii	"@"
		.ascii	LINUX_COMPILE_HOST
		.ascii	") "
		.ascii	UTS_VERSION
		db	0

! This is the default real mode switch routine.
! to be called just before protected mode transition

default_switch:
	cli			! no interrupts allowed !
	mov	al,#0x80	! disable NMI for the bootup sequence
	out	#0x70,al
	retf				!! return far (inter - segment)

! This routine only gets called, if we get loaded by the simple
! bootsect loader _and_ have a bzImage to load.
! Because there is no place left in the 512 bytes of the boot sector,
! we must emigrate to code space here.
!
bootsect_helper:
	seg cs
	cmp	word ptr bootsect_es,#0
	jnz	bootsect_second
	seg cs
	mov	byte ptr type_of_loader,#0x20
	mov	ax,es
	shr	ax,#4		!! shift right
	seg	cs
	mov	byte ptr bootsect_src_base+2,ah
	mov	ax,es
	seg cs
	mov	bootsect_es,ax
	sub	ax,#SYSSEG
	retf			! nothing else to do for now
bootsect_second:
	push	cx
	push	si
	push	bx
	test	bx,bx	! 64K full ?	!! what is the point of this? - test ALTERS ZF so if it starts as ZF=1
					!! this alters it to ZF=0 indicating bx=0
	jne	bootsect_ex
	mov	cx,#0x8000	! full 64K move, INT15 moves words
	push	cs
	pop	es
	mov	si,#bootsect_gdt
	mov	ax,#0x8700
	int	0x15		!! this moves data between the first 1Mb of memory and extended memory by 				!! means of the protected mode.  (extended memory = memory past the first 1Mb)
	jc	bootsect_panic	! this, if INT15 fails
	seg cs
	mov	es,bootsect_es	! we reset es to always point to 0x10000
	seg cs
	inc	byte ptr bootsect_dst_base+2	!! increment (add 1) the byte "bootsect_dst_base+2"
bootsect_ex:
	seg cs
	mov	ah, byte ptr bootsect_dst_base+2
	shl	ah,4	! we now have the number of moved frames in ax
	xor	al,al
	pop	bx
	pop	si
	pop	cx
	retf

bootsect_gdt:
	.word	0,0,0,0
	.word	0,0,0,0
bootsect_src:
	.word	0xffff
bootsect_src_base:
	.byte	0,0,1			! base = 0x010000
	.byte	0x93			! typbyte
	.word	0			! limit16,base24 =0
bootsect_dst:
	.word	0xffff
bootsect_dst_base:
	.byte	0,0,0x10		! base = 0x100000
	.byte	0x93			! typbyte
	.word	0			! limit16,base24 =0
	.word	0,0,0,0			! BIOS CS
	.word	0,0,0,0			! BIOS DS
bootsect_es:
	.word	0

bootsect_panic:
	push	cs
	pop	ds
	cld
	lea	si,bootsect_panic_mess
	call	prtstr
bootsect_panic_loop:
	jmp	bootsect_panic_loop	!! yes it loops back to itself
bootsect_panic_mess:
	.ascii	"INT15 refuses to access high memory.  Giving up."
	db	0

! This routine checks that the keyboard command queue is empty
! (after emptying the output buffers)
!
! Some machines have delusions that the keyboard buffer is always full
! with no keyboard attached...

empty_8042:
       push    ecx			!! the 32 bit cx
       mov     ecx,#0xFFFFFF

empty_8042_loop:
       dec     ecx			!! decrement ie subtract 1
       jz      empty_8042_end_loop

	call	delay
	in	al,#0x64	! 8042 status port
	test	al,#1		! output buffer?
	jz	no_output
	call	delay
	in	al,#0x60	! read it
	jmp	empty_8042_loop
no_output:
	test	al,#2		! is input buffer full?
	jnz	empty_8042_loop	! yes - loop
empty_8042_end_loop:
        pop     ecx
	ret

!
! Read the CMOS clock. Return the seconds in al
!
gettime:
	push	cx
	mov	ah,#0x02
	int	0x1a
	mov	al,dh			! dh contains the seconds
	and	al,#0x0f
	mov	ah,dh
	mov	cl,#0x04
	shr	ah,cl
	aad			!! ASCII adjust for division == converts two BCD numbers to a single dividend
	pop	cx
	ret

!
! Delay is needed after doing I/O
!
delay:
	.word	0x00eb			! jmp $+2
	ret

!
! Descriptor tables
!

gdt:						!! global descriptor table
	.word	0,0,0,0		! dummy

	.word	0,0,0,0		! unused

	.word	0xFFFF		! 4Gb - (0x100000*0x1000 = 4Gb)
	.word	0x0000		! base address=0
	.word	0x9A00		! code read/exec
	.word	0x00CF		! granularity=4096, 386 (+5th nibble of limit)

	.word	0xFFFF		! 4Gb - (0x100000*0x1000 = 4Gb)
	.word	0x0000		! base address=0
	.word	0x9200		! data read/write
	.word	0x00CF		! granularity=4096, 386 (+5th nibble of limit)

idt_48:					!! interrupt descriptor table register??
	.word	0			! idt limit=0
	.word	0,0			! idt base=0L

gdt_48:				!! global descriptor table register???
	.word	0x800		! gdt limit=2048, 256 GDT entries
	.word	512+gdt,0x9	! gdt base = 0X9xxxx

!
! Include video setup & detection code
!

#include "video.S"

!
! Setup signature -- must be last
!

setup_sig1:	.word	SIG1
setup_sig2:	.word	SIG2

!
! After this point, there is some free space which is used by the video mode
! handling code to store the temporary mode table (not used by the kernel).
!

modelist:

.text
endtext:
.data
enddata:
.bss
endbss:

