Search code examples
arraysassemblyx86nasm

Define a string array using NASM - Prototype give the number of days in a month


Well as the title said , I am working on a NASM project: the idea is simple , I need to take a month from input (string) and give the number of day (using cases regarding numbers). Until now I spent the day dealing with reading/printing and finally comparing 2 strings , which I could do after some struggles and thanks to some old questions I've found here and on other forums.My current problem is that I need to put the months (names) in an array so I could do the comparing with a loop, I saw on another answer that I could label the 'array' like this: label: db str1,str2 I tried that and when I try printing with just the label I get only the last month (I tried label+i but I still get the same thing) Well, here is a part of my code:

segment .data
org 100h
msg db "a"
mon1 db "janvier",0
mon2 db "fevrier",0
mon3 db "mars",0
mon4 db "avril",0
mon5 db "mai",0
mon6 db "juin",0
mon7 db "juillet",0
mon8 db "aout",0
mon9 db "septembre",0
mon10 db "octobre",0
mon11 db "novembre",0
mon12 db "decembre",24h
mo dw 1,2,3,4,5,6,7,8,9,10,11,12
mon:    
dw mon1,mon2,mon3,mon4,mon5,mon6,mon7,mon8,mon9,mon10,mon11,mon12
segment .code
    mov dx,mon
    mov ah,09h             
    int 21h

edit2: I tried the solution given by @ecm , I had to make a few change because It gave me some errors then after i could finally run it It went to error, Here is the whole code:

     segment .code
    display_month:
        ; takes month 1 to 12 in ax
        dec ax ; make number 0-based
        cmp ax,amount
        jae  error ; if out of range -->
        add ax, ax ; make it an index into a word array
        mov bx, ax ; use bx for addressing memory
        mov dx, word [mon+ bx] ; access array entry
        mov di, dx
        mov cx, -1
        mov al, 0
        repne scasb
        not cx
        dec cx ; string length
        mov bx, 1
        mov ah, 40h
        int 21h ; write to stdout
        clc ; indicate not error

        mov dx,msg1
        mov ah,09h
        int 21h
        mov ah,0Ah
        mov dx, len         ;start of buffer
        int 21h
        mov ah,02h
        mov dl,10
        int 21h
        mov dl,13
        int 21h     

        mov bx,act    
        mov dx,buffer 
        add dl,byte [bx]     
        mov bx,dx            ; move pointer into BX
        mov byte [bx],24h    ; put the $ there.

    ; compare input with msg variable(a placeholder for the moment ) I want to compare with the mon array values , and then use the index as an argument to call the cond procedure.
        mov ax,msg  
        mov si,ax
        mov ax,buffer
        mov di,ax
        cmpsb  
        jz Yep         ; if strings equal goto Yep
        jmp Nope       ; goto Nope


    Yep:
        mov dx,good
        mov ah,9
        int 21h    ; tell the user it's good
    Nope:
        mov dx,bad
        mov ah,9
        int 21h    ; tell the user it's bad
    end:
        mov  ah,4Ch
        int 21h
        ret
    error:
        stc
        retn
    cond:
        cmp ax,2
        je fev
        cmp ax,7
        jg odd
        jle even
    else:   mov bx,31
        jmp endif
    fev:    mov bx,28
    odd:    test ax,1
        jnz trente
        jmp else
    even:   test ax,1
        jp trente
        jmp else
    trente:  mov bx,30
    endif:  ret

    segment .data
    org 100h
        ;; variables declaration
    msg1 db "Veullez entrer un mois:",24h
    msg db "a",24h
    mon1:   db "janvier",0
    mon2:   db "fevrier",0
    mon3:   db "mars",0
    mon4:   db "avril",0
    mon5:   db "mai",0
    mon6:   db "juin",0
    mon7:   db "juillet",0
    mon8:   db "aout",0
    mon9:   db "septembre",0
    mon10:  db "octobre",0
    mon11:  db "novembre",0
    mon12:  db "decembre",0
    good db "Bon choix!",24h
    bad db "Mauvais choix!",24h
    mon:
     start:
        dw mon1,mon2,mon3,mon4,mon5,mon6,mon7,mon8,mon9,mon10,mon11,mon12
    tend:
    len db 254 ; a fair amount of space
    act db 0   ; will be filled with actual amount of chars received
    buffer times 254 db 0
    size equ  tend - start
    amount equ size / 2

I forgot to add how I am compiling , well I am using dosbox to run .com files , because they don't work on windows 10 , and I use the command : nasm name.asm -o name.com to create the .com file and then I just open it in dos. EDIT: Well I struggled a lot to make that happen , I couldn't do it even thought I tried different ways , the last thing I went with was to use the first month and keeping adding the size of months to pass to the other ones (i.e mon1+8 gives me mon2 ...) but then I encountered with the difference in sizes , so I changed all the months to just 3 letters (4 for june) so I could move with multiples of 4 but then I couldn't go all the way to the end .. So after that I just decided to use the mon(i) by calling the names and repeating the manoeuvre , which seemed to work thought I encountered a problem with comparing (I am still trying to figure out) , well here is the last version until I figure out how to fix that.The problem at this moment is that even if I type something different than the mon1 to mon12 I get this:Le mois de 'input' est de 30 jours while normally it should take me back to the start of the nope label.

        segment .code
org 100h
    mov dx,msg1     
    mov ah,09h
    int 21h
here:   mov ah,0Ah  
    mov dx, len     
    int 21h
    mov ah,02h  
    mov dl,10
    int 21h
    mov dl,13
    int 21h     

    mov bx,act    
    mov dx,buffer
    add dl,byte [bx]     
    mov bx,dx            
    mov byte [bx],24h    
    jmp comp
yep:
    mov dx,g1       
    mov ah,09h
    int 21h
    mov dx,buffer
    int 21h
    mov dx,g2
    int 21h
    mov ax, [i]
    call cond       
    mov ah,02h
    mov dl,bh
    int 21h
    mov dl,bl
    int 21h
    mov dx,g3
    mov ah,09h
    int 21h
    jmp end
comp:   mov bx,0
    mov [i],bx

jan:    call inct   
    mov bx,buffer
    mov si,bx
    mov bx,mon1
    mov di,bx
        cmpsb
    jz yep

fevr:   call inct
    mov bx,buffer
    mov si,bx
    mov bx,mon2
    mov di,bx
    cmpsb
        jz yep

mar:    call inct
    mov bx,buffer
    mov si,bx
    mov bx,mon3
    mov di,bx
    cmpsb
    jz yep

avr:    call inct
    mov bx,buffer
    mov si,bx
    mov bx,mon4
    mov di,bx
    cmpsb
    jz yep

mai:    call inct
    mov bx,buffer
    mov si,bx   
    mov cx,mon5     
    mov di,cx
    cmpsb
    jz yep

juin:   call inct
    mov bx,buffer
    mov si,bx
    mov bx,mon6     
    mov di,bx
    cmpsb
    jz yep
    jmp jui

jui:    call inct
    mov cx,buffer
    mov si,cx
    mov cx,mon7
    mov di,cx
    cmpsb
    jz yep

aout:   call inct
    mov cx,buffer
    mov si,cx
    mov cx,mon8
    mov di,cx
    cmpsb
    jz yep
    jmp sep

sep:    call inct
    mov bx,buffer
    mov si,bx
    mov cx,mon9
    mov di,cx
    cmpsb
    jz yep
    jmp oct

oct:    call inct
    mov bx,buffer
    mov si,bx
    mov cx,mon10
    mov di,cx
    cmpsb
    jz yep
    jmp nov

nov:    call inct
    mov bx,buffer
    mov si,bx
    mov cx,mon11
    mov di,cx
    cmpsb
    jz yep
    jmp dect

dect:   call inct
    mov bx,buffer
    mov si,bx
    mov cx,mon12
    mov di,cx
    cmpsb
    jz yep
    jmp nope
nope:
    mov dx,bad      
    mov ah,9
    int 21h
    jmp here        
end:
    mov  ah,4Ch     
    int 21h         
    ret
inct:
    push bx
    mov bx,[i]
    inc bx
    mov [i],bx
    pop bx
    ret

cond:               
    cmp ax,2
    je fev
    cmp ax,6
    je th
    cmp ax,8
    je th
    cmp ax,7
    jg odd
    jle even
else:   mov bl,'1'
    mov bh,'3'
    jmp endif
fev:    mov bl, '8'
    mov bh,'2'
    jmp endif
th: jmp else
odd:    test ax,1
    jnz trente
    jmp else
even:   test ax,1
    jp trente
    jmp else
trente:  mov bh,'3'
     mov bl,'0'
endif:  ret


segment .data
i db 0
msg1 db "Veullez entrer un mois:",24h
mon1:   db "janvier",24h
mon2:   db "fevrier",24h
mon3:   db "mars",24h
mon4:   db "avril",24h
mon5:   db "mai",24h
mon6:   db "juin",24h
mon7:   db "juillet",24h
mon8:   db "aout",24h
mon9:   db "septembre",24h
mon10:  db "octobre",24h    
mon11:  db "novembre",24h
mon12:  db "decembre",24h
g1 db "Le mois de",20h,24h
g2 db 20h,"est de",20h,24h
g3 db 20h,"jours",24h
bad db "Le mois saisi  n'est pas correct!",10,13,"Veuillez entrer un autre mois:",24h
len db 254 ; a fair amount of space
act db 0   ; will be filled with actual amount of chars received
buffer times 254 db 0


Solution

  • Try this:

    segment .code
    display_month:
        ; takes month 1 to 12 in ax
        dec ax ; make number 0-based
        cmp ax, montab.amount
        jae .error ; if out of range -->
        add ax, ax ; make it an index into a word array
        mov bx, ax ; use bx for addressing memory
        mov dx, word [montab + bx] ; access array entry
        mov di, dx
        mov cx, -1
        mov al, 0
        repne scasb
        not cx
        dec cx ; string length
        mov bx, 1
        mov ah, 40h
        int 21h ; write to stdout
        clc ; indicate not error
        retn
    
    .error:
        stc
        retn
    
    segment .data
    mon1: db "janvier",0
    mon2: db "fevrier",0
    mon3: db "mars",0
    mon4: db "avril",0
    mon5: db "mai",0
    mon6: db "juin",0
    mon7: db "juillet",0
    mon8: db "aout",0
    mon9: db "septembre",0
    mon10: db "octobre",0
    mon11: db "novembre",0
    mon12: db "decembre",0
    
        align 2
    montab:
    .:
        dw mon1
        dw mon2
        dw mon3
        dw mon4
        dw mon5
        dw mon6
        dw mon7
        dw mon8
        dw mon9
        dw mon10
        dw mon11
        dw mon12
    .end:
    .size equ .end - .
    .amount equ .size / 2
    

    You could use 12 as the hardcoded array length in this case. But using equates for the length and amount of entries is useful for static data arrays more generally.

    ETA: I added alignment for the table, which is good for performance. Not needed but it doesn't cost much.

    Note that I dropped the org directive from my example. This is because I am presenting just one function that should be called from other program logic. If you're assembling into a simple style flat .COM executable for 86-DOS, you still need to include the org 256 at some point.


    Here's a breakdown of your question's attempt:

    msg db "a"
    

    This seems like an unused leftover.

    mon1 db "janvier",0
    mon2 db "fevrier",0
    mon3 db "mars",0
    mon4 db "avril",0
    mon5 db "mai",0
    mon6 db "juin",0
    mon7 db "juillet",0
    mon8 db "aout",0
    mon9 db "septembre",0
    mon10 db "octobre",0
    mon11 db "novembre",0
    

    These are all fine, they define ASCIZ strings. (The Z stands for zero-terminated.)

    mon12 db "decembre",24h
    

    This one uses a different terminator, in this case the dollar sign (24h = 36). It is not appropriate to mix terminators between array entries. Either use all ASCIZ or all CP/M-style dollar-terminated strings.

    mo dw 1,2,3,4,5,6,7,8,9,10,11,12
    

    This is useless. If you wanted to map a number from 1 to 12 as index into this array, the original value would be found at that array entry. If the entries had different numbers this type of array could be useful, but not with identity mapping.

    mon:    
    dw mon1,mon2,mon3,mon4,mon5,mon6,mon7,mon8,mon9,mon10,mon11,mon12
    

    This is essentially correct. (I put each entry on its own line and prepended the align directive, but neither is absolutely necessary.)

        mov dx,mon
        mov ah,09h             
        int 21h
    

    This loads the address of your message table into dx then passes dx to interrupt 21h function 09h. This function expects a dollar-terminated string, so only your December string would work. More importantly, it will try to display the literal bytes that make up your array. You need to, instead, dereference a pointer to one of your array's entries to load the address stored therein, which is the address of the associated message string.