В вашем основном методе вы пытаетесь получить доступ, например, к club
(который защищен), когда вы должны получить доступ к myclub
, который является публичным свойством, которое вы создали.
Не могу комментировать, поэтому я отправляю ответ таким образом. @Ira Baxter, отличный ответ. Я просто хочу добавить, что вам не нужно разделить 10 раз, когда вы разместили, что вы установили регистр cx в значение 10. Просто разделите число в топе до «ax == 0»
loop1: call dividebyten
...
cmp ax,0
jnz loop1
Вам также нужно сохранить количество цифр в исходном номере.
mov cx,0
loop1: call dividebyten
inc cx
В любом случае, вы помогли мне Ире Бакстер, есть только несколько способов оптимизации кода:)
Это касается не только оптимизации, но и форматирования. Если вы хотите напечатать номер 54, вы хотите напечатать 54 не 0000000054:)
Вам нужно написать двоичную процедуру преобразования в десятичный код, а затем использовать десятичные цифры для создания «цифровых символов» для печати.
Вы должны предположить, что что-то где-то будет печатать символ на ваше устройство вывода по выбору. Вызовите эту подпрограмму «print_character»; предполагает, что он принимает код символа в EAX и сохраняет все регистры .. (Если у вас нет такой подпрограммы, у вас есть дополнительная проблема, которая должна быть основой другого вопроса).
Если у вас есть двоичный код для цифры (например, значение от 0 до 9) в регистре (скажем, EAX), вы можете преобразовать это значение в символ для цифры, добавив код ASCII для символа «нуль», чтобы регистр. Это также просто:
add eax, 0x30 ; convert digit in EAX to corresponding character digit
Затем вы можете вызвать print_character для печати символьного кода.
Чтобы вывести произвольное значение, вам нужно выбрать цифры и распечатать их .
Выбор цифр принципиально требует работы с полномочиями в десять. Проще всего работать с одной мощностью десять, например, самой 10. Представьте, что у нас есть подпрограмма «разделить на 10», которая взяла значение в EAX, и произвела коэффициент в EDX и остальную часть EAX. Я оставляю это как упражнение для вас, чтобы выяснить, как реализовать такую процедуру.
Тогда простая процедура с правильной идеей состоит в том, чтобы произвести одну цифру для всех цифр, которые могут иметь значение. 32-битный регистр хранит значения до 4 миллиардов, поэтому вы можете напечатать 10 цифр. Итак:
mov eax, valuetoprint
mov ecx, 10 ; digit count to produce
loop: call dividebyten
add eax, 0x30
call printcharacter
mov eax, edx
dec ecx
jne loop
Это работает ... но печатает цифры в обратном порядке. К сожалению! Ну, мы можем воспользоваться стеком pushdown для хранения произведенных цифр, а затем вывести их в обратном порядке:
mov eax, valuetoprint
mov ecx, 10 ; digit count to generate
loop1: call dividebyten
add eax, 0x30
push eax
mov eax, edx
dec ecx
jne loop1
mov ecx, 10 ; digit count to print
loop2: pop eax
call printcharacter
dec ecx
jne loop2
Слева в качестве упражнения для читателя: подавляйте ведущие нули. Кроме того, поскольку мы пишем цифры в памяти, вместо того, чтобы записывать их в стек, мы можем записать их в буфер, а затем распечатать содержимое буфера. Также оставлен в качестве упражнения для читателя.
1 -9 равны 1 -9. после этого должно быть какое-то преобразование, о котором я тоже не знаю. Скажем, у вас есть 41H в AX (EAX), и вы хотите распечатать 65, а не «A», не выполняя какой-либо служебный вызов. Я думаю, вам нужно напечатать представление символа 6 и 5, что бы это ни было. Должно быть постоянное число, которое можно добавить туда. Вам нужен оператор модуля (как вы это делаете в сборке) и цикл для всех цифр.
Не уверен, но это мое предположение.
'0'
до '9'
смежны, поэтому вы можете просто вычислить 6 + '0'
, чтобы получить '6'
. т. е. использовать div
или что угодно, чтобы получить остаток, затем add edx, '0'
и сохранить этот байт в буфер. '0'
= 0x30
, но большинство ассемблеров принимают символьные константы, поэтому лучше писать код таким образом. (Это может быть полезно для OR
или AND
вместо ADD
/ SUB
, что также работает, потому что 0x30
не имеет ни одного из его низких 4 битов.)
– Peter Cordes
19 September 2017 в 03:52
Я полагаю, вы хотите напечатать значение для stdout? Если это так, вы должны использовать системный вызов для этого. Системные вызовы зависят от ОС.
, например. Linux: Таблица системных вызовов Linux
Всемирная программа hello в этом учебнике может дать вам некоторые идеи.
Большинство операционных систем / сред не имеют системного вызова, который принимает целые числа и преобразует их в десятичные для вас. Вы должны сделать это самостоятельно, прежде чем отправлять байты в ОС или копировать их в видеопамять самостоятельно или рисовать соответствующие символы шрифта в видеопамяти ...
На сегодняшний день наиболее эффективным способом является создание один системный вызов, который выполняет всю строку сразу, потому что системный вызов, который записывает 8 байтов, в основном такой же, как и запись 1 байта.
Это означает, что нам нужен буфер, но это не добавляет к нашей сложности вообще. 2 ^ 32-1 - это только 4294967295, что составляет всего 10 десятичных цифр. Наш буфер не должен быть большим, поэтому мы можем просто использовать стек.
Обычный алгоритм производит цифры LSD-first. Поскольку порядок печати MSD-first, мы можем просто начать работу в конце буфера и работать в обратном направлении. Для печати или копирования в другом месте просто отслеживайте, где он начинается, и не беспокойтесь о том, чтобы получить его до начала фиксированного буфера.
char *itoa_end(unsigned long val, char *p_end) {
const unsigned base = 10;
char *p = p_end;
do {
*--p = (val % base) + '0';
val /= base;
} while(val); // runs at least once to print '0' for val=0.
// write(1, p, p_end-p);
return p; // let the caller know where the leading digit is
}
gcc / clang выполняет отличную работу, использует магический постоянный множитель ] вместо div
, чтобы эффективно делить на 10. ( Godbolt compiler explorer для выхода asm).
Вот прокомментированная версия NASM, в которой используется div
(медленный, но более короткий код) для 32-разрядных целых чисел без знака и Системный вызов Linux write
. Это должно быть легко переносить на 32-битный режим, просто изменив регистры на ecx
вместо rcx
. (Вы также должны сохранить / восстановить esi
для обычных 32-битных соглашений вызова, если только вы не делаете это в функции макроса или внутреннего использования.)
Часть системного вызова специфичный для 64-разрядной Linux. Замените это на все, что подходит для вашей системы, например. вызовите страницу VDSO для эффективных системных вызовов в 32-разрядной Linux или используйте int 0x80
непосредственно для неэффективных системных вызовов. См. вызывающие соглашения для 32 и 64-битных системных вызовов в Unix / Linux .
ALIGN 16
; void print_uint32(uint32_t edi)
; x86-64 System V calling convention. Clobbers RSI, RCX, RDX, RAX.
global print_uint32
print_uint32:
mov eax, edi ; function arg
mov ecx, 0xa ; base 10
push rcx ; newline = 0xa = base
mov rsi, rsp
sub rsp, 16 ; not needed on 64-bit Linux, the red-zone is big enough. Change the LEA below if you remove this.
;;; rsi is pointing at '\n' on the stack, with 16B of "allocated" space below that.
.toascii_digit: ; do {
xor edx, edx
div ecx ; edx=remainder = low digit = 0..9. eax/=10
;; DIV IS SLOW. use a multiplicative inverse if performance is relevant.
add edx, '0'
dec rsi ; store digits in MSD-first printing order, working backwards from the end of the string
mov [rsi], dl
test eax,eax ; } while(x);
jnz .toascii_digit
;;; rsi points to the first digit
mov eax, 1 ; __NR_write from /usr/include/asm/unistd_64.h
mov edi, 1 ; fd = STDOUT_FILENO
lea edx, [rsp+16 + 1] ; yes, it's safe to truncate pointers before subtracting to find length.
sub edx, esi ; length, including the \n
syscall ; write(1, string, digits + 1)
add rsp, 24 ; undo the push and the buffer reservation
ret
Public domain. Не стесняйтесь копировать / вставлять это во все, над чем вы работаете. Если он ломается, вы можете сохранить обе части.
И вот код, который вызывается в цикле, считая до 0 (включая 0). Вставить его в один и тот же файл.
ALIGN 16
global _start
_start:
mov ebx, 100
.repeat:
lea edi, [rbx + 0] ; put whatever constant you want here.
call print_uint32
dec ebx
jge .repeat
xor edi, edi
mov eax, 231
syscall ; sys_exit_group(0)
Собрать и связать с
yasm -felf64 -Worphan-labels -gdwarf2 print-integer.asm &&
ld -o print-integer print-integer.o
./print_integer
100
99
...
1
0
Использовать strace
, чтобы увидеть, что единственными системными вызовами этой программы являются write()
и exit()
. (См. Также советы gdb / debugging в нижней части вики x86 , а также другие ссылки там.)
Я опубликовал версию AT & amp; T-syntax из этого для 64-битных целых чисел в качестве ответа на Печать целого числа в виде строки с синтаксисом AT & amp; T с системными вызовами Linux вместо printf . См. Это для получения дополнительных комментариев о производительности и эталонного кода div
по сравнению с компилятором с использованием mul
.