У меня есть задача, и я постараюсь объяснить ее понятно. Есть файл с [0; 1000] строк. Каждая строка содержит 6 столбцов.
Первые два столбца содержат строку с [1; 20] символов. Символами могут быть буквы, цифры и пробелы.
3-5 столбцы содержат целые числа в диапазоне [-100; 100]. 6-й столбец содержит действительные числа в диапазоне [-9,99; 9,99] только с двумя цифрами после запятой.
Каждый раздел я разделял точкой с запятой ';'.
ПРИМЕР ФАЙЛА:
helloA;lB;lC;lD;lE;lF
A11;bas morning;0;0;5;1.15
B12; Hello WoRlD;-100;11;78;1.33
B11;table;10;0;55;-2.44
C1;OakWood;0;8;17;3.77
ЗАДАНИЕ: подсчитайте, сколько строк в первых двух секциях содержит буквы «В» и «С». И напечатайте это целое число в другом файле.
Я сделал почти все задание, кроме одного. Я не знаю, как напечатать десятичное число в файле. Я храню это число в памяти как шестнадцатеричное. Мне нужно преобразовать это число в десятичное и распечатать его в другом файле.
Я борюсь, потому что может быть 1 хорошая строка, но также может быть и 1000 хороших строк. Итак, мне нужно напечатать 1 символ (если количество хороших строк находится между [0; 9]), но это может быть 900 хороших строк, поэтому программа должна напечатать 3 символа.
МОЙ КОД
org 100h
%include 'yasmmac.inc'
section .text
startas:
macPutString 'Output file:', crlf, '$'
; Save the writing file's name
mov al, 128
mov dx, writingFile
call procGetStr
macNewLine
; Open reading file
mov dx, readingFile
call procFOpenForReading
jnc .writingFileOpen
macPutString 'Error while opening the writing file!', '$'
exit
; Open the writing file
.writingFileOpen:
mov [readingDescriptor], bx
mov dx, writingFile
call procFCreateOrTruncate
jnc .writingFileSuccessfullyOpened
macPutString 'Error while opening file for writing!', '$'
jmp .writingError
; Sacing writing descriptor
.writingFileSuccessfullyOpened:
mov [writingDescriptor], bx
; Read first line
call procReadLine
; Main loop
.untilEndOfFile:
call procReadLine
; checking the first two columns
;mov al, ';'
; checking first column
.firstColumn:
mov al, [di]
inc di
cmp al, byte 'B'
je .skipALine
cmp al, byte 'b'
je .skipALine
cmp al, byte 'C'
je .skipALine
cmp al, byte 'c'
je .skipALine
cmp al, byte ';'
jne .firstColumn
; checking second column
.secondColumn:
mov al, [di]
inc di
cmp al, byte 'B'
je .skipALine
cmp al, byte 'b'
je .skipALine
cmp al, byte 'C'
je .skipALine
cmp al, byte 'c'
je .skipALine
cmp al, byte ';'
jne .secondColumn
jmp .addNumber ; Adding number because line corresponds to filter.
.addNumber:
call procAddNumber
; If it is not the end of file, jump back to main loop
.skipALine:
cmp [readTheLastLine], byte 0
je .untilEndOfFile
; Writing to file (number, how many good lines)
; **I cant do this part**
mov bx, [writingDescriptor]
mov cx, 2h
mov dx, lineCount
mov ah, 40h
int 21h
; Closing Files
.end:
mov bx, [writingDescriptor]
call procFClose
.writingError:
mov bx, [readingDescriptor]
call procFClose
exit
%include 'yasmlib.asm'
; void procReadLine()
; Read line to buffer 'line'
procReadLine:
push ax
push bx
push cx
push si
mov bx, [readingDescriptor]
mov si, 0
.loop:
call procFGetChar
; End if the end of file or error
cmp ax, 0
je .endOfFile
jc .endOfFile
; Putting symbol to buffer
mov [line+si], cl
inc si
; Check if there is \n?
cmp cl, 0x0A
je .endOfLine
jmp .loop
.endOfFile:
mov [readTheLastLine], byte 1
.endOfLine:
mov [line+si], byte '$'
mov [lineLength], si
pop si
pop cx
pop bx
pop ax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
procAddNumber:
push si
push ax
push bx
push cx
push dx
;lineCount++
mov ax, [lineCount]
inc ax
mov [lineCount], ax
pop dx
pop cx
pop bx
pop ax
pop si
ret
section .data
readingFile:
db 'input.dat', 00
readingDescriptor:
dw 0000
writingFile:
times 128 db 00
writingDescriptor:
dw 0000
readTheLastLine:
db 00
line:
db 64
times 66 db '$'
lineLength:
dw 0000
lineCount:
dw 0000
GitHub ссылка на макросы: yasmlib.asm/yasmmac.inc
Любая помощь будет оценена по достоинству.
Я не знаю, как напечатать десятичное число в файле. Я храню это число в памяти как шестнадцатеричное. Мне нужно преобразовать это число в десятичное и распечатать его в другом файле.
Решение проблемы уже есть в файле yasmlib.asm! Он содержит код procUInt16ToStr, который преобразует беззнаковое число в AX в строку ASCIIZ по адресу в DX.
Он не возвращает длину строки, поэтому вам придется перебирать строку и использовать procFPutChar для отправки отдельных символов в файл. В качестве альтернативы и предпочтительно перебрать строку, чтобы установить длину строки и вывести все сразу с помощью функции DOS 40h (как вы уже делали).
Если вам интересно узнать, как преобразовать целое число в строку, вы можете прочитать мой вопрос/ответ Отображение чисел в DOS.
.WritingToFile:
mov dx, Buffer
mov ax, [linecount]
call procUInt16ToStr ; produces an ASCIIZ string at DX
mov si, dx
.again:
lodsb
cmp al, 0
jne .again
sub si, dx
lea cx, [si - 1] ; -1 because we don't want to send the zero
mov bx, [writingDescriptor]
mov ah, 40h ; DOS.WriteToFile
int 21h ; -> AX CF
.untilEndOfFile: call procReadLine .firstColumn: mov al, [di]
Этот код использует DI без инициализации регистра (mov di, line
).
.skipALine: cmp [readTheLastLine], byte 0 je .untilEndOfFile
Проверка переменной readTheLastLine происходит слишком поздно! Это нужно сразу после возврата из процедуры procReadLine:
.untilEndOfFile:
call procReadLine
cmp byte [readTheLastLine], 0
je .WritingToFile
mov di, line
.firstColumn:
mov al, [di]
...
jmp .untilEndOfFile
.WritingToFile:
Вам не нужна эта расточительная процедура procAddNumber для увеличения переменной linecount.
Просто замените call procAddNumber
на inc word [linecount]
.
@Kurbamit Убедитесь, что переменная не определена как 8-битный байт. В исходной программе lineCount определяется как 16-битное слово, проверьте это. Обычно YASM следует за NASM, поэтому inc word [lineCount]
должно быть хорошо, но вы также можете попробовать inc word ptr [lineCount]
(как это делает MASM). И в крайнем случае попробуйте: mov ax, [lineCount]
inc ax
mov [lineCount], ax
.
@Kurbamit Является ли YASM чувствительным к регистру? В своем ответе я использовал linecount, но вы использовали lineCount. Будьте осторожны с копипастом!
В то время как другой ответ касался вашего вопроса о написании текстового представления числа, этот ответ фокусируется на фундаментальном непонимании того, что задает задача.
ЗАДАНИЕ: подсчитайте, сколько строк в первых двух секциях содержит буквы «В» и «С».
Ваша текущая программа подсчитывает строки, которые не содержат ни «B», ни «C». Противоположное тому, что спрашивали. Далее вы получите количество строк, содержащих либо «B», либо «C».
.untilEndOfFile:
call procReadLine
cmp byte [readTheLastLine], 0
je .WritingToFile
...
jne .secondColumn
jmp .untilEndOfFile
.skipALine:
inc word [linecount]
jmp .untilEndOfFile
.WritingToFile:
Обратите внимание, что приведенное выше по-прежнему не буквально «содержат буквы «B» и «C», это больше похоже на «содержат буквы «B» или «C»». Но не волнуйтесь, различие, которое очень важно в программировании, не всегда может считаться столь важным в повседневной устной/письменной речи.
Сейчас увидел один баг в этой программе. Каким-то образом максимальное значение, которое может получить lineCount, равно 255. Если хороших строк больше, чем 255, он перезапускает счетчик. У Вас есть мысли, почему так могло быть?