460 Lab Assignment #1

                   CS560 Lab Assignment #1 
###########################################################################
                    DUE : in 2 Weeks
###########################################################################
   
1. REQURIEMENT:
   To deveop a boot program for booting the MTX operating system.

2. Background Reading List
   Notes #2: Booting

3-1. Download the MTX image file
         http://www.eecs.wsu.edu/~cs460/cs560/samples/LAB1/mtximage
     Use it as VIRTUAL FD, as in
        qemu-system-i386 -fda mtximage -no-fd-bootchk
     Then, boot up MTX from the virtual FD.

     Test run the MTX operating system as demonstrated in class.

                         CONTENTS of the MTX disk image:

     |  B0  | B1 ...................................................... B1339 |
     --------------------------------------------------------------------------
     |booter|   An EXT2 file system for MTX; kernel=/boot/mtx                 | 
     --------------------------------------------------------------------------
   
     LAB#1 IS FOR YOU TO WRITE A BOOTER PROGRAM TO REPLACE THE booter IN BLOCK#0
     TO BOOT UP THE MTX KERNEL, which is the image file /boot/mtx.

3-2. Background: Computer Architecture and Programming Environment
     Lab#1 assumes the following hardware and software environments.

     Hardware: Intel X86 based PC running Linux. For convenience, use a virtual
               machine that emulates the PC hardware: QEMU, VMware, VirtualBox,
               etc.
     Software: BCC compiler-assembler-linker under Linux. 

     When a PC starts, it is in the so-called 16-bit UNPROTECTED mode, also 
     known as the 16-bit real mode. While in this mode, the PC's CPU can only 
     execute 16-bit code and access 1MB memory. The diagram below shows the 1MB
     memory layout, shown in 64KB segments.

                      1MB MEMORY layout in 64KB SEGMENTS

           0x0000    0x1000   .......     0x9000     0xA000 ... 0xF000
           -----------------------------------------------------------
           |         |     ..........     |          |          BIOS |
           -----------------------------------------------------------
           |<-------------- 640KB RAM area --------->|<--- ROM ----->|

     The CPU's internal registers are
              segment registers: CS, DS, SS, ES 
              general registers: AX, BX, CX, DX, BP, SI, DI
              status  register : FLAG
              stack pointer    : SP
              instruction point or program counter: IP
     All registers are 16-bit wide. 
  
     The CPU operates as follows:

1. In real-mode, the CPU has 20-bit address lines for 2**20 = 1MB memory, e.g.
   20-bit addresses
                    0x00000, 0x00010, 0x00020
                    0x10000, 0x20000, 0x30000, etc.

   A segment is a block of memory beginning from a 16-byte boundary. Since the 
   last 4 bits of a segment address are always 0, it suffices to represent a
   segment address by the leading 16 bits. Each segment size is up to 64KB.

2. The CPU in has 4 segment registers, each 16-bits.

       CS -> Code  segment  = program code or instructions
       DS -> Data  segment  = static and global data (ONE COPY only)
       SS -> Stack segment  = stack area for calling and local variables. 
       ES -> Extra segment  = temp area; may be used for malloc()/mfree()

3. In a program, every address is a 16-bit VIRTUAL address (VA). For each 16-bit
   VA, the CPU automatically translates it into a 20-bit PHYSICAL address (PA)
   by
          (20-bit)PA = ((16-bit)segmentRegister << 4) + (16-bit)VA.
   
  where segmentRegister is either by default or by a segment prefix in the 
  instruction.
  Examples:
  Assume CS=0x1234. IP=0x2345 ==> PA = (0x1234<<4) + 0x2345 = 0x14685 (20 bits)
         DS=0x1000. mov ax,0x1234 ==> PA=0x10000 + 0x1234   = 0x11234, etc.

IMPORTANT: In a program, every address is a 16-bit VA, which is an OFFSET in a
           memory segment. When accessing memory, the CPU maps every VA to a 
           20-bit PA.

4. The number of DISTINCT segments available to the CPU depends on the
   memory model of the executing program, which is determined by the compiler
   and linker used to generate the binary executable image. The most often used
   memory models are

    One-segment model :(COM files): CS=DS=SS all in ONE segment <= 64KB
    Separate I&D model:(EXE files): CS=CodeSegment, DS=SS=Data+Stack segment 
  
One-segment model programs can be loaded to, and executed from, any available 
segment in memory. In order to run a One-segment memory model program, the 
following steps are needed:

(1). A C compiler and assembler which generate 16-bit (object) code
(2). A linker that combines the object code to generate a ONE-segment
     binary executable image. We shall use BCC under Linux to do (1) and (2).
(3). LOAD the binary executable image into memory (at a segment boundary) 
     and set CPU's CS=DS=SS = loaded segment.
         Set SP at the HIGH end of the segment.
         Set IP at the beginning instruction in the segment.
     Then let the CPU execute the image.
 

5. PRE-WORK #1: DUE: in one week

5.1. Download files from samples/LAB1/LAB1.1/

Given: The following bs.s file in BCC's assembly

!==================== bs.s file ===================================
        .globl _main             ! IMPORT symbols from C code
        .globl _getc,_putc       ! EXPORT symbols to C code

!--------------------------------------------------------------------
! Only one SECTOR loaded at (0000,7C00). Load entire block to 0x90000
!--------------------------------------------------------------------
        mov  ax,#0x9000    ! set ES to 0x9000
        mov  es,ax

        xor  bx,bx          ! clear BX = 0
!--------------------------------------------------------------------
!  Call BIOS INT-13 to read BOOT BLOCK to (segment,offset)=(0x9000,0)
!--------------------------------------------------------------------
        xor  dx,dx          ! DH=head=0,   DL=drive=0
        xor  cx,cx          ! CL=cylinder, CL=sector
        incb cl             ! BIOS counts sector from 1
        mov  ax, #0x0202    ! AH=READ      AL=2 sectors
        int  0x13           ! call BIOS INT-13

        jmpi start,0x9000   ! CS=0x9000, IP=start

start:                    
        mov  ax,cs          ! Set segment registers for CPU
        mov  ds,ax          ! we know ES,CS=0x9000. Let DS=CS  
        mov  ss,ax          ! SS = CS ===> all point at 0x9000
        mov  es,ax
        mov  sp,#8192       ! SP = 8192 above SS=0x9000

!--------------- OPTIONAL --------------------------------------
        mov  ax,#0x0012     ! Call BIOS for 640x480 color mode     
	int  0x10           ! 
!--------------------------------------------------------------	
        call _main          ! call main() in C

        jmpi 0,0x1000
 

!======================== I/O functions =================================
		
!---------------------------------------------
!  char getc()   function: returns a char
!---------------------------------------------
_getc:
        xorb   ah,ah           ! clear ah
        int    0x16            ! call BIOS to get a char in AX
        ret 

!----------------------------------------------
! void putc(char c)  function: print a char
!----------------------------------------------
_putc:           
        push   bp
	mov    bp,sp
	
        movb   al,4[bp]        ! get the char into aL
        movb   ah,#14          ! aH = 14
        movb   bl,#0x0D        ! bL = color 
        int    0x10            ! call BIOS to display the char

        pop    bp
	ret

Write YOUR own t.c file in C:

/************** t.c file **************************/
int prints(char *s)
{
    call putc(c) to print string s;
}

int gets(char s[ ])
{
    call getc() to input a string into s[ ]
}

main()
{
   char name[64];
   while(1){
     prints("What's your name? ");
     gets(name);
     if (name[0]==0)
        break;
     prints("Welcome "); prints(name); prints("\n\r");
   }
   prints("return to assembly and hang\n\r");
}

5-3. Use BCC to generate a one-segment binary executable a.out WITHOUT header
     
     as86 -o bs.o  bs.s
     bcc -c -ansi  t.c
     ld86 -d bs.o t.o /usr/lib/bcc/libc.a

5-4. dump a.out to a VIRTUAL FD disk:

     dd if=a.out of=mtximage bs=1024 count=1 conv=notrunc

5-5. Boot up QEMU from the virtual FD disk:
     
     qemu-system-i386 -fda mtximage -no-fd-bootchk


6. PRE-WORK #2: DUE: in ONE week
   Download files from samples/LAB1/LAB1.2

   mtximage contains an EXT2 file system, block size = 1KB (1024 bytes)

     |  B0  |  B1   B2.....................       ..................... B1339 |
     --------------------------------------------------------------------------
     |booter| SUPER|GD |Bmap|Imap|INODES                               |      |
     -------------------------------------------------------------------------- 
   
   Write your C code to print all the file names in the root directory /

             The complete bs.s file:
==========================================================================
        BOOTSEG =  0x9000        ! Boot block is loaded again to here.
        SSP      =   8192        ! Stack pointer at SS+8KB
	
        .globl _main,_prints              ! IMPORT symbols
        .globl _getc,_putc                ! EXPORT symbols

	.globl _readfd,_setes,_inces,_error  
	                                                
        !-------------------------------------------------------
        ! Only one SECTOR loaded at (0000,7C00). Get entire BLOCK in
        !-------------------------------------------------------
        mov  ax,#BOOTSEG    ! set ES to 0x9000
        mov  es,ax
        xor  bx,bx          ! clear BX = 0

        !---------------------------------------------------
        !  diskio[0,0] to read boot BLOCK to [0x9000,0]     
        !---------------------------------------------------
        xor  dx,dx          ! drive 0, head 0
        xor  cx,cx
        incb cl             ! cyl 0, sector 1
        mov  ax, #0x0202    ! READ 1 block
        int  0x13

        jmpi    start,BOOTSEG           ! CS=BOOTSEG, IP=start

start:                    
        mov     ax,cs                   ! Set segment registers for CPU
        mov     ds,ax                   ! we know ES,CS=0x9000. Let DS=CS  
        mov     ss,ax                   ! SS = CS ===> all point at 0x9000
        mov     es,ax
        mov     sp,#SSP                 ! SP = 8KB above SS=0x9000

        mov     ax,#0x0012              ! 640x480 color     
	int     0x10 
	
        call _main                      ! call main() in C
     
        test ax, ax
	je  _error 

        jmpi 0,0x1000
 

!======================== I/O functions =================================

        !---------------------------------------------
        !  char getc()   function: returns a char
        !---------------------------------------------
_getc:
        xorb   ah,ah           ! clear ah
        int    0x16            ! call BIOS to get a char in AX
        ret 

        !----------------------------------------------
        ! void putc(char c)  function: print a char
        !----------------------------------------------
_putc:           
        push   bp
	mov    bp,sp
	
        movb   al,4[bp]        ! get the char into aL
        movb   ah,#14          ! aH = 14
        movb   bl,#0x0D        ! bL = cyan color 
        int    0x10            ! call BIOS to display the char

        pop    bp
	ret

       !---------------------------------------
       ! readfd(cyl, head, sector, buf)
       !         4     6     8     10
       !---------------------------------------
_readfd:                             
        push  bp
	mov   bp,sp            ! bp = stack frame pointer

        movb  dl, #0x00        ! drive 0=FD0
        movb  dh, 6[bp]        ! head
        movb  cl, 8[bp]        ! sector
        incb  cl               ! BIOS count sector from 1
        movb  ch, 4[bp]        ! cyl
        mov   bx, 10[bp]       ! BX=buf ==> memory addr=(ES,BX)
        mov   ax, #0x0202      ! READ 2 sectors to (EX, BX)

        int  0x13              ! call BIOS to read the block 
        jb   _error            ! to error if CarryBit is on [read failed]

        pop  bp                
	ret
        
_setes: push  bp
        mov   bp,sp

        mov   ax,4[bp]        
        mov   es,ax

	pop   bp
	ret
	   
_inces:                         ! inces() inc ES by 0x40, or 1K
        mov   ax,es
        add   ax,#0x40
        mov   es,ax
        ret

        !------------------------------
        !       error & reboot
        !------------------------------
_error:
        mov  bx, #bad
        push bx
        call _prints
        
        int  0x19                       ! reboot

bad:    .asciz  "Error!"
==================================================================
The getblk(u16 blk, char *buf) function:

int getblk(u16 blk, char *buf)
{
    readfd( (2*blk)/CYL, ( (2*blk)%CYL)/TRK, ((2*blk)%CYL)%TRK, buf);
}

The booter runs in the segment 0x9000. 
CS, DS, SS, ES are all pointing at the same segment 0x9000. 
BIOS will load the disk block blk to (ES, buf), which is the char buf[ ] in 
the program.
=========================================================================

7. Modify YOUR t.c in LAB1.2 to get the INODE of /boot/mtx, i.e.

   let    INODE *ip -> INODE of /boot/mtx

   ip->i_blokc[0] to ip->i_block[11] are DIRECT blocks

   ip->i_block[12] = IDIRECT block = an array of 32-bit integers, 0 if no more.

To load the blocks into memory starting from (segment) 0x1000

   setes(0x1000);            // ES = 0x1000 => BIOS loads disk block to (ES, buf)
 
loop:
   getblk((u16)blkno, 0);    // buf = 0 => memory address = (ES, 0)
   inces();                  // inc ES by 1KB/16 = 0x40    
repeat loop for next blkno until no more
   

8. LAB1 main work: DUE: in 2 weeks 

   Modify t.c file to boot up /boot/mtx from the mtximage VIRTUAL disk

sample solution: mtximage  (run qemu on it)