Author Topic: Help me with reducing the code  (Read 17000 times)

Offline mare011

  • Jr. Member
  • *
  • Posts: 2
Help me with reducing the code
« on: November 02, 2010, 12:49:20 PM »
Can someone help me, i need to reduce my code to be < 150 bytes when compile it in .com
The program works in real mode, and it draws the time in the upper right corrner and it's TSR.
I basicly install my interupt vector 1c that writes the time with yellow color, i'm writing directly in graphic memory, cause i'm forbbiden to use int 21h, and then i call 27h so it becomes TSR
My code is now 165 bytes, i need to lose 15 bytes, does anyone have an idea how to do it, i basicly stript every redundant code i could find, but i'm not experiensed in assembly, so i guss that there's a liter way of doing some instructions.

Here's the code:
Code: [Select]
org 100h
segment .code

main:
call    _install_1C
mov dx, load
int 27h


; -----------------------------------------------------------------------
; Install vector 1c
; -----------------------------------------------------------------------


_install_1C:
;cli
xor ax, ax
mov es, ax
; Point vector table to my rutine
mov dx, timer_int
mov [es:1Ch*4], dx
mov ax, cs
mov [es:1Ch*4+2], ax
push ds
pop gs
sti        
ret


timer_int:
pushaw
push gs
pop ds
; Timer interupt code
call _get_time                    ; my function to get time
call _print_time                  ; my function to print time

izlaz:
popaw
iret



; -----------------------------------------------------------------------
; _get_time_string -- gets time in format: hh:mm:ss
; -----------------------------------------------------------------------

%define T_SEPARATOR   ':'

_get_time:
        pusha
        mov     di, 0
        mov     ah, 2                     ; Getting BIOS time in BCD
        int     1Ah                         ; CH: hours, CL: minutes, DH: seconds

        mov     al, ch                      ; Hours
        shr     al, 4                      
        and     ch, 0Fh                      
        call    AddDigit
        mov     al, ch                      
        call    AddDigit
        mov     al, T_SEPARATOR
        call    AddSymbol
        mov     al, cl                      
        shr     al, 4
        and     cl, 0Fh
        call    AddDigit
        mov     al, cl
        call    AddDigit
        mov     al, T_SEPARATOR
        call    AddSymbol
        mov     al, dh                      ; Secunds
        shr     al, 4
        and     dh, 0Fh
        call    AddDigit
        mov     al, dh
        call    AddDigit
        mov     al, 0                       ; end of string
        call    AddSymbol
        popa
        ret


AddDigit:  
        add     al, '0'                    ; coverts AL into ASCII
        mov [vreme + di], al            
inc di
        ret
AddSymbol:
mov [vreme + di], al            
inc di  
        ret


; -----------------------------------------------------------------------
;Putting time in video memory
; -----------------------------------------------------------------------


zuta equ 0Eh                             ; Yellow color


_print_time:
        push    es
        mov     ax, 0B800h                  ; Baze adress of video momory = B8000h
        mov     es, ax
        mov     bx, word 144                ; Video position = end of line 2 is 160 - 16 bayte, 8 position since one position uses 2 bytes.
mov si, vreme    ; si point to array 'vreme' (time)

.prn:
        mov     al, byte [si]    
        cmp     al, 0
        je .end
        mov     byte [es:bx], al
        inc     bx
        mov     al, zuta    
        mov     byte [es:bx], al
        inc     bx
        inc     si
        jmp     .prn
.end:
        pop     es
        ret

load:

segment .data

vreme: db 0

Thanks in advance

Offline Rob Neff

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 429
  • Country: us
Re: Help me with reducing the code
« Reply #1 on: November 02, 2010, 10:14:05 PM »
Briefly looking at your code, the following snippet optimizes out a few operations for you:

Code: [Select]
AddDigit: 
   add     al, '0'                     ; coverts AL into ASCII
AddSymbol:
   mov [vreme + di], al           
   inc di    
   ret

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Help me with reducing the code
« Reply #2 on: November 02, 2010, 10:26:55 PM »
Nice job. It's a shame to uglify it to save a few bytes. But there are some things you can do (or not do)...

Code: [Select]
org 100h
segment .code

main:
call    _install_1C
mov dx, load
int 27h


; -----------------------------------------------------------------------
; Install vector 1c
; -----------------------------------------------------------------------


_install_1C:
;cli
xor ax, ax
mov es, ax
; Point vector table to my rutine
mov dx, timer_int
mov [es:1Ch*4], dx

Could be:

Code: [Select]
        mov word [es:1Ch * 4], timer_int

And...

Code: [Select]
mov ax, cs
mov [es:1Ch*4+2], ax

Could be:
Code: [Select]
        mov [es:1Ch * 4 + 2], cs


Code: [Select]
push ds
pop gs
sti         
ret

I think I see why you "push ds"/"pop gs", but you don't really need to do it. Might not work, anyway. When the interrupt is triggered, we don't know what ds might have been, so we want to set it to a known value. But gs might have been changed, too! We do know what cs is, and we want ds to be the same, so we could use cs instead of gs here. That doesn't save any bytes (except we didn't need to do gs, above). Worse, pushaw/popaw doesn't preserve segment registers, and we need to do it, so add "push ds" and "pop ds" at the end (of your timer_int). We're losing ground! :(

I notice that you've got "cli" commented out. Was it causing a problem? We don't want that interrupt to occur when we've changed the offset but not the segment! But Windows may not like "cli"(?). If that's a problem, we could do it "atomically":

Code: [Select]
...
mov ax, cs
shl eax, 16
mov ax, timer_int
mov [es:1Ch * 4], eax
...

Yeah, you can use 32-bit registers in 16-bit code. It requires an "operand size override prefix" - 66h - which Nasm will provide. I'd stick with the "cli", if it works.

Code: [Select]
timer_int:
pushaw
push gs
pop ds
; Timer interupt code
call _get_time                    ; my function to get time
call _print_time                  ; my function to print time

izlaz:
popaw
iret



; -----------------------------------------------------------------------
; _get_time_string -- gets time in format: hh:mm:ss
; -----------------------------------------------------------------------

%define T_SEPARATOR   ':'

_get_time:
        pusha
        mov     di, 0
        mov     ah, 2                     ; Getting BIOS time in BCD
        int     1Ah                         ; CH: hours, CL: minutes, DH: seconds

        mov     al, ch                      ; Hours
        shr     al, 4                       
        and     ch, 0Fh                     
        call    AddDigit
        mov     al, ch                     
        call    AddDigit
        mov     al, T_SEPARATOR
        call    AddSymbol
        mov     al, cl                     
        shr     al, 4
        and     cl, 0Fh
        call    AddDigit
        mov     al, cl
        call    AddDigit
        mov     al, T_SEPARATOR
        call    AddSymbol
        mov     al, dh                      ; Secunds
        shr     al, 4
        and     dh, 0Fh
        call    AddDigit
        mov     al, dh
        call    AddDigit
        mov     al, 0                       ; end of string
        call    AddSymbol
        popa
        ret


AddDigit: 
        add     al, '0'                     ; coverts AL into ASCII
        mov [vreme + di], al           
inc di
        ret
AddSymbol:
mov [vreme + di], al           
inc di    
        ret


; -----------------------------------------------------------------------
;Putting time in video memory
; -----------------------------------------------------------------------


zuta equ 0Eh                             ; Yellow color


_print_time:
        push    es
        mov     ax, 0B800h                  ; Baze adress of video momory = B8000h
        mov     es, ax
        mov     bx, word 144                ; Video position = end of line 2 is 160 - 16 bayte, 8 position since one position uses 2 bytes.
mov si, vreme     ; si point to array 'vreme' (time)

.prn:
        mov     al, byte [si]    
        cmp     al, 0
        je .end
        mov     byte [es:bx], al
        inc     bx
        mov     al, zuta    
        mov     byte [es:bx], al
        inc     bx
        inc     si
        jmp     .prn
.end:
        pop     es
        ret

load:

segment .data

vreme: db 0

You might want to reserve a bigger buffer for "vreme" - you're putting 8 bytes into it. Accumulating your "string" and printing it "direct to screen" as you do is a nice "clean" way to do it. But, strictly speaking, you don't need to do that - you could put the digits and ':'s right to the screen, instead of into your string. This saves a bunch of bytes - "don't do it at all" is shorter, faster... even easier. But it's uglier (IMO)!

The so-called "string instructions" are nice and short. "lodsb" replaces "mov al, [si]" and "inc si". "stosb" replaces "mov [es:di]" and "inc si". "stosw" is handy here - if we've got the color in ah, and the character in al, and the video segment in es, and the desired offset in di, then "stosw" will print character and color, and add 2 to di, all in one byte! You use "[es:bx]", but "[es:di]" would work, then you could use "stosb" twice, or "stosw" once...

I've mutilated your code into this - untested(!) which assembles into about 80 bytes - a little more could be done, I think, but this is "short enough" (maybe... that "int 27h" you use is "obsolete" and has some limitations...)

Code: [Select]
org 100h

%define T_SEPARATOR   ':'
zuta equ 0Eh                             ; Yellow color

segment .text

main:
call    _install_1C
mov dx, load
int 27h


; -----------------------------------------------------------------------
; Install vector 1c
; -----------------------------------------------------------------------


_install_1C:
cli
xor ax, ax
mov es, ax
; Point vector table to my rutine
mov word [es:1Ch * 4], timer_int
mov [es:1Ch*4+2], cs

sti         
ret

; Timer interupt code
timer_int:
pushaw
push es

push 0B800h
pop es


        mov     ah, 2                     ; Getting BIOS time in BCD
        int     1Ah                         ; CH: hours, CL: minutes, DH: seconds

mov ah, zuta
        mov     di, word 144                ; Video position = end of line 2 is 160 - 16 bayte, 8 position since one position uses 2 bytes.

        mov     al, ch                      ; Hours
call print_two_digits

        mov     al, T_SEPARATOR
stosw

        mov     al, cl                     
call print_two_digits

        mov     al, T_SEPARATOR
stosw

        mov     al, dh                      ; Secunds
call print_two_digits


izlaz:
pop es
popaw
iret

print_two_digits:
; expects: char in al, color in ah, video segment in es, offset in di
; returns: al trashed, di set to next position

push ax
shr al, 4
add al, '0'
stosw
pop ax
and al, 0Fh
add al, '0'
stosw
ret

load:

Since I've eliminated the string, we don't even care what ds is in the interrupt routine, so I've eliminated that, too... I really think your code is a lot "nicer". Perhaps you can develop a "hybrid" that works...

Best,
Frank


Offline mare011

  • Jr. Member
  • *
  • Posts: 2
Re: Help me with reducing the code
« Reply #3 on: November 03, 2010, 07:40:23 PM »
Wow, thanks Frank! It all looks so logical and simple, but i would probably never think of writing it that way. You really help me a lot, plus I've really learned a lot by looking at your code and comments. BTW i'd commented the "cli" instuction to see how much bytes will it free up, and i forgot to uncomment it :)