1

I have this code for ASM that is supposed to replicate the scanf(); function. But I have a feeling that there is something wrong with the code. Here is the code:

  _scanf:
  MOV DS, 0
  JMP .continue
  RET

.continue
  MOV AH, 00h
  INT 0x16
  CMP AH, 0Dh
  JE .backspace
  CMP AL, 0x0D
  JE .done
  CMP AL, 0x0A
  JE .done
  MOV CX, 1
  MOV BH, 0
  MOV AH, 09h
  INT 0x10
  MOV AH, 03h
  INT 0x10
  ADD DL, 1
  MOV AH, 02h
  INT 0x10
  MOV WORD [DX+DS], AL
  ADD DS, 1
  JMP .continue

.done:
  RET

.backspace:
  MOV WORD [DX+DS], 0x00
  MOV AH, 03h
  INT 0x10
  SUB DL, 1
  MOV AH, 02h
  INT 0x10
  MOV BH, 0
  MOV AL, 0x00
  MOV CX, 1
  MOV AH, 09h
  INT 0x10
  JMP .continue

Is this valid code? Or is the [DX+DS] opcode an invalid segment override?

Using compiler NASM with 16-bit code.

  • The assembler chokes on it, right? So definitely not valid. `DS` is the default anyway so there would be no need to use it as an override. But also, x86-16 can't use `DX` in an effective address; see https://stackoverflow.com/questions/12474010/nasm-x86-16-bit-addressing-modes. You could use `[DI]` instead. And `ADD DS,1` is invalid, maybe a typo for `DX`? – Nate Eldredge Jan 22 '21 at 02:43
  • Same for `mov dx, 0` - the is no mov-immediate to segment register, only `mov Sreg, reg/mem`. *Is this valid code?* - you could have checked yourself to see if it would even assemble, and found out that the answer was no. Then you'd know which lines to start researching about. – Peter Cordes Jan 22 '21 at 02:44
  • 1
    And you're trying to use DX both as a pointer and as the cursor position simultaneously. That's no good. – Nate Eldredge Jan 22 '21 at 02:45
  • 1
    It seems that nasm has some sort of multi-pass system, where a more severe error on a later line might get reported, but not other fatal errors on earlier lines. So I only get one error message at first (for `MOV [DX+DS], AL`) but after fixing that I get some more. – Nate Eldredge Jan 22 '21 at 02:47
  • @NateEldredge DS is actually for adding the offset. And DX is not the cursor pos, DH and DL is. – Hornet's Channel Jan 22 '21 at 03:53
  • How about using SI instead of DX? – Hornet's Channel Jan 22 '21 at 04:00
  • (1) DX *is* DH and DL! They are the two halves of DX. (2) You can't use segment registers that way. Are you familiar with how x86-16 segmentation works? If you really set DS to 0, you will be writing your string over the interrupt vector table. There is an awful lot of confusion in your code; I think you may want to start with something simpler. – Nate Eldredge Jan 22 '21 at 04:13
  • Oh, sorry about that. But is SI a good choice? – Hornet's Channel Jan 22 '21 at 04:19
  • How about I go with `[SI+DI]`? – Hornet's Channel Jan 22 '21 at 04:21
  • I fixed most of the errors, but there are invalid segment overrides at line 32 and 41. – Hornet's Channel Jan 22 '21 at 04:25
  • `[SI+DI]` is not encodable, see https://stackoverflow.com/questions/12474010/nasm-x86-16-bit-addressing-modes. You can't just guess at instructions, you need to check an actual instruction set reference to see what does and doesn't exist. Next, I'm not sure why you should need or want to have segment overrides anywhere in this code. What you do need is some memory where you can safely put your string. – Nate Eldredge Jan 22 '21 at 04:49

1 Answers1

1

In the category "wrong" you have:

  • MOV DS, 0 not allowed on segment registers.
  • MOV WORD [DX+DS], AL not a valid addressing mode and also a mismatch between the 'WORD' tag and the byte size of AL.
  • ADD DS, 1 not allowed on segment registers.
  • MOV WORD [DX+DS], 0x00 not a valid addressing mode.

In the category "eyebrow" you have:

  • JMP .continue followed by RET making this RET unreachable.
  • Outputting via BIOS.WriteCharacterAndAttribute function 09h without specifying an actual attribute in the BL register. If you're unlucky you won't see anything appear on the screen.
  • Backspacing without checking if there's anything (left) to backspace over.

In the category "nit-picking" you have:

  • Comparing for backspace via the scancode in AH when it's shorter (and more usual) to do this via the ASCII code 8 stored in AL.
  • Writing a word-sized 0 in memory when you receive a backspace. A byte would be enough and nothing at all would be fine too.
  • Writing ASCII character 0 on the screen to remove a character. ASCII code 32 is the normal way to do this.

Below is a simplified version of your code. Make sure you have a buffer somewhere in your program. Use something like buffer times 80 db 0.
Since you don't seem to care about the color attribute, you can just as well use the Teletype function and not update the cursor yourself. Teletyping an ASCII code 8, moves the cursor to the left, but you have to write the space character yourself.

_scanf:
  mov  bx, 0007h    ; CONST DisplayPage and GraphicsColor
  mov  cx, 1        ; CONST ReplicationCount
  mov  di, buffer
.continue:
  mov  ah, 00h      ; BIOS.WaitKey
  int  16h          ; -> AX
  cmp  al, 8
  je   .backspace
  cmp  al, 13
  je   .done
  cmp  al, 10
  je   .done
  mov  [di], al     ; Write in buffer
  inc  di           ; Add to buffer
  mov  ah, 0Eh      ; BIOS.Teletype
  int  10h
  jmp  .continue
.backspace:
  cmp  di, buffer
  jna  .continue
  dec  di           ; Remove from buffer
  mov  ah, 0Eh      ; BIOS.Teletype (backspace)
  int  10h
  mov  ax, 0A20h    ; BIOS.WriteCharacter (space)
  int  10h
  jmp  .continue
.done:
  mov  byte [di], 0 ; Zero-terminate the string
  ret
Sep Roland
  • 33,889
  • 7
  • 43
  • 76