В тази статия ще се задълбочим в процеса на създаване на преобразувател на дроб към десетичен знак, процент и обратен преобразувател за 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.