В этой статье мы углубимся в процесс создания преобразователя дробей в десятичные, проценты и обратно для Commodore 64 с использованием языка Ассемблера (6510/6502). Этот проект не только обеспечивает практическое применение навыков программирования на ассемблере, но и позволяет нам изучить ностальгическое очарование компьютеров 80-х годов.

Постановка задачи

В объятиях ностальгии по 80-м

Путешествуя по машине времени, мы попадаем в 80-е годы, где написание математических программ для Commodore 64 — это не просто хобби, а часть школьного курса. В этой статье мы отправляемся в путь по созданию преобразователя дробей — на первый взгляд простого, но познавательного проекта, позволяющего отточить наши навыки языка ассемблера.

Эта статья продолжает тему, которую я начал в предыдущих статьях:

Решение

Создание прототипа BASIC

Традиционно мы начинаем реализацию с прототипа на BASIC. Прототип служит основой, позволяющей нам визуализировать структуру и общий дизайн программы.

Два примечательных аспекта прототипа включают в себя:

  • Реализация меню: Мы определяем параметры меню.
  • Вычисление экспоненты: использование длины текстового представления числа для вычисления экспоненты.

Переход на язык ассемблера

Рассмотрев многочисленные детали в предыдущих статьях, мы сосредоточимся на логике высокого уровня нашего решения Ассамблеи, а также на принятых нами инновационных подходах.

Обработка меню

После настройки экрана и отображения всплывающей подсказки функция отображения меню занимает центральное место в качестве исходной функции программы. Эта функция эффективно отображает пункты меню и обрабатывает ввод пользователя. Если пользователь вводит неверный параметр, программа корректно его игнорирует. Впоследствии он вызывает соответствующую процедуру на основе ввода пользователя.

main:
        jsr     prepare_screen
        jsr     main_usage
menu:
        jsr     show_menu
wait_for_continue:
        jsr     getin
        beq     wait_for_continue
        cmp     #q_sym
        beq     go_to_exit
        cmp     #one_sym
        beq     handle_decimal
        cmp     #two_sym
        beq     handle_fraction
        cmp     #three_sym
        beq     handle_percent
        jsr     clearscreen
        jmp     menu
handle_decimal:
        jsr     decimal_handler
        jmp     continue_or_exit
handle_fraction:
        jsr     fraction_handler
        jmp     continue_or_exit
handle_percent:
        jsr     percent_handler
        jmp     continue_or_exit
go_to_exit:
        jmp     restore_and_exit

continue_or_exit:
        jsr     usage_at_exit
wait_for_input:
        jsr     getin
        beq     wait_for_input
        cmp     #q_sym
        bne     continue
        jmp     restore_and_exit
continue:
        jsr     clearscreen
        jmp     menu

restore_and_exit:
        jsr     restore_screen

        rts

Десятичное преобразование

decimal_handler:
        jsr     input_decimal
        jsr     cursor_blink_off
        jsr     calculate_den
        lda     #<n
        ldy     #>n
        jsr     fp_load_ram_to_fac
        lda     #<den
        ldy     #>den
        jsr     fp_mult
        ldx     #<num
        ldy     #>num
        jsr     fp_store_fac_to_ram
        lda     #<hundred
        ldy     #>hundred
        jsr     fp_load_ram_to_fac
        lda     #<n
        ldy     #>n
        jsr     fp_mult
        ldx     #<per
        ldy     #>per
        jsr     fp_store_fac_to_ram
        jsr     show_results
        rts

input_decimal:
        jsr     input_decimal_prompt
        jsr     input_string_proc
        jsr     string_to_fp
        ldx     #<n
        ldy     #>n
        jsr     fp_store_fac_to_ram
        jsr     get_exponent_by_counter
        lda     #space_sym
        jsr     print_char
        lda     #new_line
        jsr     print_char
        rts

Наша процедура обработки десятичной дроби принимает десятичную дробь в качестве аргумента и преобразует ее в дробь и процент. Эта процедура начинается с процедуры ввода, аналогичной той, что обсуждалась ранее. Ключевое отличие заключается в вычислении показателя степени на основе введенного количества символов.

get_exponent_by_counter:
        ldx     counter
        dex
        txa
        sta     e_counter
        lda     number_strings
        clc
        adc     counter
        sta     address
        dec     address
        lda     #<(address)
        sta     $22
        lda     #>(address)
        sta     $23
        lda     #string_length
        jsr     fp_string_to_fac
        ldx     #<e
        ldy     #>e
        jsr     fp_store_fac_to_ram
        rts

Эта процедура выполняет следующее:

  • Уменьшает значение счетчика на единицу и сохраняет полученное значение в счетчиках экспоненты.
  • Загружает в аккумулятор адрес начала массива строкового представления чисел.
  • Добавляет к результату счетчик введенных символов, сохраненный в переменной.
  • Поскольку смещение отсчитывается от 0, значение уменьшается на единицу.
  • Далее загружается полный адрес элемента массива со смещением.
  • Строка по указанному адресу преобразуется в число с плавающей запятой и сохраняется в переменной e (показатель степени).

Массив числовых строк выглядит следующим образом:

number_strings:
        .text   "0"
        .byte   $0
        .text   "1"
        .byte   $0
        .text   "2"
        .byte   $0
        .text   "3"
        .byte   $0
        .text   "4"
        .byte   $0
        .text   "5"
        .byte   $0
        .text   "6"
        .byte   $0
        .text   "7"
        .byte   $0
        .text   "8"
        .byte   $0
        .text   "9"
        .byte   $0
        .text   "10"
        .byte   $0
        .text   "11"
        .byte   $0
        .text   "12"
        .byte   $0
        .text   "13"
        .byte   $0
        .text   "14"
        .byte   $0
        .text   "15"
        .byte   $0

Далее в обработчике десятичной дроби вызывается процедура вычисления знаменателя.

calculate_den:
        ldx     e_counter
        dex
        beq     handle_tenth
        txa
        sta     counter
        lda     #<ten
        ldy     #>ten
        jsr     fp_load_ram_to_fac
mult_loop:
        lda     #<ten
        ldy     #>ten
        jsr     fp_mult
        ldx     counter
        dex
        beq     end_loop
        txa
        sta     counter
        jmp     mult_loop
end_loop:
        ldx     #<den
        ldy     #>den
        jsr     fp_store_fac_to_ram
        jmp     end_mult
handle_tenth:
        lda     #<ten
        ldy     #>ten
        jsr     fp_load_ram_to_fac
        ldx     #<den
        ldy     #>den
        jsr     fp_store_fac_to_ram

end_mult:
        rts

Эта процедура вычисляет десять, приведенную в показатель степени, используя умножение и значение e_counter, полученное в предыдущей процедуре.

Далее я получаю числитель, умножая введенное число на знаменатель.
А умножая введенное число на 100, получаю проценты.

Далее я вывожу результат отдельной процедурой.

show_results:
        lda     #space_sym
        jsr     print_char
        lda     #new_line
        jsr     print_char
        lda     #<result_1
        ldy     #>result_1
        jsr     print_str
        lda     #<n
        ldy     #>n
        jsr     fp_load_ram_to_fac
        jsr     fp_to_str
        jsr     print_str
        lda     #new_line
        jsr     print_char
        lda     #<result_2
        ldy     #>result_2
        jsr     print_str
        lda     #<num
        ldy     #>num
        jsr     fp_load_ram_to_fac
        jsr     fp_to_str
        jsr     print_str
        lda     #<result_3
        ldy     #>result_3
        jsr     print_str
        lda     #<den
        ldy     #>den
        jsr     fp_load_ram_to_fac
        jsr     fp_to_str
        jsr     print_str
        lda     #new_line
        jsr     print_char
        lda     #<result_4
        ldy     #>result_4
        jsr     print_str
        lda     #<per
        ldy     #>per
        jsr     fp_load_ram_to_fac
        jsr     fp_to_str
        jsr     print_str
        lda     #new_line
        jsr     print_char
        lda     #new_line
        jsr     print_char
        rts

Ограничения этой реализации:

  • Функция работает только с дробями ‹ 1.
  • Формат ввода «.123» без ведущего нуля.

Обработка фракций

Процессор дробей принимает аргументы числителя и знаменателя, преобразуя их в десятичные дроби и проценты.

fraction_handler:
        jsr     input_num_den
        jsr     cursor_blink_off
        lda     #<den
        ldy     #>den
        jsr     fp_load_ram_to_fac
        lda     #<num
        ldy     #>num
        jsr     fp_div
        ldx     #<n
        ldy     #>n
        jsr     fp_store_fac_to_ram
        lda     #<hundred
        ldy     #>hundred
        jsr     fp_load_ram_to_fac
        lda     #<n
        ldy     #>n
        jsr     fp_mult
        ldx     #<per
        ldy     #>per
        jsr     fp_store_fac_to_ram
        jsr     show_results
        rts

Десятичная дробь получается путем деления числителя на знаменатель.
Проценты получаются путем умножения десятичной дроби на 100.

Процентная конверсия

Обработчик процентов принимает процентные значения в качестве входных данных и преобразует их в дробные и десятичные числа.

percent_handler:
        jsr     input_percents
        lda     #<hundred
        ldy     #>hundred
        jsr     fp_load_ram_to_fac
        lda     #<per
        ldy     #>per
        jsr     fp_div
        ldx     #<n
        ldy     #>n
        jsr     fp_store_fac_to_ram
        lda     #<per
        ldy     #>per
        jsr     fp_load_ram_to_fac
        ldx     #<num
        ldy     #>num
        jsr     fp_store_fac_to_ram
        lda     #<hundred
        ldy     #>hundred
        jsr     fp_load_ram_to_fac
        ldx     #<den
        ldy     #>den
        jsr     fp_store_fac_to_ram
        jsr     show_results
        rts

Десятичная дробь получается путем деления процента на 100.
Обычный: в числителе процент введен знаменатель 100.

У этой функции есть ограничение: она может корректировать только целые проценты.

Заключение

В этом путешествии по математическому миру Commodore 64 мы разработали преобразователь дробей на языке ассемблера. Полный исходный код этой программы вы можете найти [здесь].

Вот пример исполнения

Бинарный файл имеет размер 3220 байт.

Хотя школьная математика может показаться базовой, она обеспечивает отличную площадку для оттачивания навыков программирования на ассемблере. Этот проект продемонстрировал творческие возможности, которые предлагают эпоха 80-х и Commodore 64.

Продолжая это увлекательное путешествие, я приглашаю вас остаться со мной и отправиться в новые математические приключения в мире программирования для Commodore 64.