Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

HDD info

Status
Not open for further replies.

chigley

Programmer
Sep 30, 2002
104
0
0
GB
Hi,

I am teaching myself assembly. I have books on order by am leafing my way through code snippets and trying to piece stuff together. The first exercise I have set myself is to try and get the basic HDD info from the first (80h) HDD in the machine. I am using TASM and running in DOS (16 bit)
I can make the call to the int13/08H operation successfully, my problem comes when I try and echo the results to the screen. I am using a put_str function I found, and a put_num function. The problem is that they always return 0 :-(

So I get

Number of heads = 0
Number of cylinders = 0
Number of sectors = 0

Code:
mov     ax, offset driveinfomessage
call    put_str
		
push    cs              ; setup DS so we can access
pop     ds              ; our codeseg variables without
                                        ; the CS segment override
push    cs
pop     es              ; While we're at it, fix ES too!                

mov     ah,08H          ; get drive parameters
mov     dl,80H          ; for drive 80H (harddisk1)
int     13H

mov     al,dh
xor     ah,ah
inc     ax
mov     Heads,ax        ; save head count
	
mov     al,cl           ; put sector number in AL
and     ax,3FH          ; and mask off cylinder bits
mov     Sectors,ax      ; save sector count
		
mov     al,ch           ; AL = cylinder LO
mov     ah,cl           ; AH = cylinder HI...
mov     cl,6
shr     ah,cl           ; shift the HI bits down
inc     ax
mov     Cylinders,ax    ; save cylinder count
		
mov     ax,Sectors      ; multiply "sectors"
mul     Heads           ; byte "heads"
mul     Cylinders       ; and then by "cylinders"

        ; save total capacity in sectors!
mov     WORD PTR Capacity[0],ax
mov     WORD PTR Capacity[2],dx
		
		mov ax, @data
		mov ds, ax
		
		;print out the number of cylinders
		mov     ax, offset numcylindersmessage
		call    put_str
		mov     ax, Cylinders
		call    print_num
		mov     ax, offset newline
		call    put_str
		
		;print out the number of heads
		mov     ax, offset numheadsmessage
		call    put_str
		mov     ax, Heads
		call    print_num
		mov     ax, offset newline
		call    put_str
		
		;print out the number of sectors
		mov     ax, offset numsectorsmessage
		call    put_str
		mov     ax, Sectors
		call    print_num
		mov     ax, offset newline
		call    put_str

For completeness the code for the put_str function

Code:
put_str: ; display string terminated by 0 
		; whose address is in ax
push ax ; save registers
push bx 
push cx
push dx
mov bx, ax ; store address in bx
mov al, byte ptr [bx] ; al = first char in string
put_loop: cmp al, 0 ; al == 0 ?
je put_fin ; while al != 0
  call putc ; display character	
  inc bx ; bx = bx + 1
  mov al, byte ptr [bx] ; al = next char in string
  jmp put_loop ; repeat loop test	
put_fin: 
		
pop dx ; restore registers
pop cx
pop bx
pop ax	
ret

and the print_num function

Code:
; this procedure prints number in AX, 
	; used with PRINT_NUM_UNS to print signed numbers: 
	print_num proc near
        push    dx
        push    ax

        cmp     ax, 0
        jnz     not_zero

        putchar "0"
        jmp     printed

	not_zero:
        ; the check SIGN of AX, 
        ; make absolute if it's negative: 
        cmp     ax, 0
        jns     positive
        neg     ax

        putchar "-"

	positive:
        call    print_num_uns
	printed:
        pop     ax
        pop     dx
        ret
	print_num endp

	; this procedure prints out an unsigned 
	; number in AX (not just a single digit) 
	; allowed values are from 0 to 65535 (FFFF) 
	print_num_uns proc near
        push    ax
        push    bx
        push    cx
        push    dx

        ; flag to prevent printing zeros before number: 
        mov     cx, 1

        ; (result of "/ 10000" is always less or equal to 9). 
        mov     bx, 10000       ; 2710h - divider. 

        ; AX is zero? 
        cmp     ax, 0
        jz      print_zero

	begin_print:

        ; check divider (if zero go to end_print): 
        cmp     bx,0
        jz      end_print

        ; avoid printing zeros before number: 
        cmp     cx, 0
        je      calc
        ; if AX<BX then result of DIV will be zero: 
        cmp     ax,bx
        jb      skip
	calc:
        mov     cx, 0   ; set flag. 

        mov     dx,0
        div     bx      ; AX = DX:AX / BX   (DX=remainder). 

        ; print last digit 
        ; AH is always ZERO, so it's ignored 
        add     al,30h    ; convert to ASCII code. 
        putchar al    

        mov     ax,dx  ; get remainder from last div. 

	skip:
    
		; calculate BX=BX/10 
        push    ax
        mov     dx, 0
        mov     ax, bx
        div     cs:ten  ; AX = DX:AX / 10   (DX=remainder). 
        mov     bx,ax
        pop     ax

        jmp  begin_print

	print_zero:
		putchar "0"

	end_print:

        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
	print_num_uns endp
	
	print_al proc
		cmp al, 0
		jne print_al_r
		push ax
		mov al, '0'
		mov ah, 0eh
		int 10h
		pop ax
		ret
	print_al_r:
		push ax
		mov ah, 0
		cmp ax, 0
		je pn_done
		mov dl, 10
		div dl
		call print_al_r
		mov al, ah
		add al, 30h
		mov ah, 0eh
		int 10h
		jmp pn_done
	pn_done:
		pop ax
		ret
	endp

Charlie Benger-Stevenson
Hart Hill IT Ltd
 
Part of the problem you have some data in the CodeSeg and some in the DataSeg. When you go to print out, you are accessing DataSeg:Heads when you want to access CodeSeg:Heads. Try moving your CodeSeg variables into the DataSeg, and eliminate the DS register change to the CodeSeg at the top of the listing. Also, since there is nothing prior to that point that destroys DS, the restoring of DS is uneccessary prior to printing.
 
(1) Have you checked that your number-writing procedures actually work correctly with known numbers. For instance, if you do mov ax, 23 and call the number-writing procedure, what does it output?

(2) If it works OK, it'd be a good idea to get it to output the number of cylinders, sectors and heads individually to check what numbers you are getting back. If anything has become zero, then the result of all your multiplies will come back as zero.

(3) On the cs = ds issue, if you are writing a .com tiny-model program then naturally cs = ds and it really doesn't matter whether you refer to things in cs or ds, except that an explicit mention of cs adds a byte to the instruction (and so far as I know slows things down very slightly).
 
Did I point out I was a noob? Not really sure what Prattaratt means. Lionelhill, the answers you require are

1) Yes it works. Although only for numbers up to 9999.
2) All report zero, before going to the multiplication step.
3) Not sure that this matters, or that I understand.

I have found the emu8086.inc file on the web, which would be better to use I think. I wonder if the asm gurus would share what they do for utility functions, is emu8086.inc the industry standard? Or is there something better? I tried to include teh emu3086.inc file using an include directive but got compile time errors using TASM.

Charlie Benger-Stevenson
Hart Hill IT Ltd
 
oh, right. It's years since I've gone anywhere near the disk-drive interrupts because usually dos does enough for me. Someone else here must know more. Chances are they're not doing the job you think they're doing on your system. It could even be that they're being suppressed by windows because, were they working, they'd allow direct physical access to anywhere on your hard disk, obviously just the sort of access that pleases those of a destructive nature, and not the sort of thing a reputable operating system would care to encourage.
 
What I was trying to point out in my earlier posting is that you MAY be trying to access data in the wrong area. Although the subsequent posts lead me to believe your primary problem lies with your Hardware or OS (will explain later), I want to clarify my earlier posting.
In your code you have the following code:
Code:
...
push    cs              ; setup DS so we can access
pop     ds              ; our codeseg variables without
                                        ; the CS segment override
...

so any data accesses without a segm overide from this point will be accessing the code segment. In a tiny segment model, they are the same as the data segment. all other models they are not. Before you start to call the printout routines, you have the following:
Code:
...
        
        mov ax, @data
        mov ds, ax
...
This sets the DS register back the default Data Segment, which we have noted may or may not be the same as the Code segment, which is where you have stored the HDD characteristics. So to access both at the same time you will need to use a CS override to get at the code seg variables, or use another system of accessing the data (Read up on using the si and di registers, Hint , Hint ;-)). So while this is not the primary cause of your problem, this may or may not (depending on the Segment Model you used to assemble in) print the correct data.

Now as to why you are getting only zeroes for your interrupt results, if the Hard drive you have is above a certain size, you have to use an extended BIOS call. I can't remember off the top of my head what that size is or what the extended BIOS call is, but I do know if the cylinder count is excess of 16383, it's going to overflow.

Also, if you are using DOS from Windows(I believe 98 and newer versions, but correct me if I am wrong), Windows locks you out from using the BIOS hard drive interrupts for security reasons.
 
Awesome post!!!!

I will read up on si and di registers, books are on their way from US - its cheaper but a longer wait for us in the UK :)

I need to get my head around what you are saying, but certainly the put_num function does not print any number larger than 9999, obviously.

What I really need is the guidance on how to do common stuff like chucking out to screen, is emu8086.inc the way to go? Or is there a "theworldsbestulilityfunctionsfor8086processors.inc" I should be using?

Thanks

Charlie Benger-Stevenson
Hart Hill IT Ltd
 
Good post, prattaratt: Oops, I didn't even spot the restoring of ds. A case of not seeing what I didn't expect to see; there shouldn't be any need to restore ds unless you're working in a memory model where different bits of your program are using different data segments.

If you're doing this for self-education, you'd probably do best to skip anything refering to low-level disk manipulation since it's very unlikely to be useful to you unless you want to get into the esoteric areas of defragmentation tools and dos-style virus-writing. There are much more fun things to do with assembly.

If you haven't come across him yet, the writings of Abrash (Zen of assembler etc.) are a good read, if a little dated. Maybe he's produced more up-to-date stuff. The last I read was about the time Pentiums were newish.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top