Compare commits

..

2 Commits

Author SHA1 Message Date
Matthias@Dell
e1f78b2927 split into driver and handler 2024-01-01 14:56:11 +01:00
Matthias@Dell
7f909b4f75 add parity 2023-12-31 14:56:24 +01:00
10 changed files with 534 additions and 181 deletions

View File

@ -3,7 +3,7 @@
.include "lcd.h65"
.include "math.h65"
.include "keypad.h65"
.include "keyboard.h65"
.include "keyboard_handler.h65"
.include "chars.h65"
.import homeloop:absolute
.import home:absolute
@ -25,47 +25,49 @@ CODE_START:
DEBUG_LED_OFF 1
DEBUG_LED_OFF 2
lda #<kb_irq1
sta ARG0
lda #>kb_irq1
sta ARG1
lda #<$3000
sta ARG2
lda #>$3000
sta ARG3
StoreDByte kb_irq1, ARG0
StoreDByte $3000, ARG2
; lda #<kb_irq1
; sta ARG0
; lda #>kb_irq1
; sta ARG1
; lda #<$3000
; sta ARG2
; lda #>$3000
; sta ARG3
ldy #10
jsr memcopy
lda #<kb_irq2
sta ARG0
lda #>kb_irq2
sta ARG1
lda #<$3100
sta ARG2
lda #>$3100
sta ARG3
StoreDByte kb_irq2, ARG0
StoreDByte $3100, ARG2
; lda #<kb_irq2
; sta ARG0
; lda #>kb_irq2
; sta ARG1
; lda #<$3100
; sta ARG2
; lda #>$3100
; sta ARG3
ldy #10
jsr memcopy
; PrintNC $3000
jsr kb::init
jsr ps2kb::init
stz kb::status
StoreDByte process_scancode,ps2kb::scancode_handler
jsr ps2kb::begin_receive
lda #'%'
jsr lcd::print_char
stz kp::_DEBUG_VAL
ldy #0
@loop:
wai
lda kb::scancode
lda ps2kb::scancode
beq @noscancode
jsr process_scancode
stz kb::scancode
stz ps2kb::scancode
lda char
beq @onlykeycode
Strf fmt_codechar,out_str,keycode,kb::status,char
@ -131,7 +133,7 @@ CODE_START:
iny
jmp @loop
@lA:
lda kb::KB_IO + IO::SR
lda ps2kb::VIA + IO::SR
jsr lcd::print_char
jmp @loop
@lB:
@ -154,7 +156,7 @@ CODE_START:
kb_irq1:
; lda #'!'
; jsr lcd::print_char
jsr kb::irq_shift_reg_handler
jsr ps2kb::_receive_irq_shift_reg_handler
; lda #':'
; jsr lcd::print_char
rts
@ -163,7 +165,7 @@ kb_irq2:
; lda #'?'
; jsr lcd::print_char
; jsr kb::on_timer_irq
jsr kb::irq_timer_handler
jsr ps2kb::_receive_irq_timer_handler
; lda #';'
; jsr lcd::print_char
rts
@ -172,7 +174,7 @@ kb_irq2:
.proc process_scancode
; DEBUG_LED_OFF 1
; check how this scancode needs to be interpreted
ldx kb::scancode
ldx ps2kb::scancode
bit kb::status
jmi release_key ; bit 7 set
bvs @twobytecode ; bit 6 set

View File

@ -109,5 +109,24 @@ INCLUDE_IOW65C22 = 1
IRQ = %10000000
.endenum
;;********************************************************************************
;; @macro Enable an interrupt source
;; @modifies: A
;; @param flag: A flag of the interrupt flag register (IO:IRQ)
;;********************************************************************************
.macro IO_EnableIRQ ioaddr, flag
lda #(IO::IRQ::IRQ | flag)
sta ioaddr + IO::IER
.endmacro
;;********************************************************************************
;; @macro Disable an interrupt source
;; @modifies: A
;; @param flag: A flag of the interrupt flag register (IO:IRQ)
;;********************************************************************************
.macro IO_DisableIRQ ioaddr, flag
lda #flag
sta ioaddr + IO::IER
.endmacro
.endscope ; IO
.endif ; guard

View File

@ -1,138 +0,0 @@
.include "keyboard.h65"
.include "string.h65"
.include "lcd.h65"
Export kb,init,irq_shift_reg_handler,irq_timer_handler,scancode,key_read
Export kb, CHARS_NOMOD, CHARS_MODSHIFT
ExportZp kb,status
.zeropage
status:
.bss
key_read: .res 2
scancode: .res 1
.code
;;********************************************************************************
;; @function Initialize the PS2 keyboard
;; @modifies: A
;;********************************************************************************
.proc init
; - use the shift register interrupts to read the first 8 bits
; set shift register to shift in under external clock on CB1
; - configure timer for timing the read of the last 3 bits
; timer 2 one shot mode is sufficient, leaves T1 available
lda #(IO::ACR::SR_SIN_PHIE | IO::ACR::T2_IRQ_LOAD)
tsb kb::KB_IO + IO::ACR
; the 3 last bits take about 230us, at @1MHz => wait 230 cycles and then the shift register
; (this could be shorter since the it takes a few cycles after the interrupt)
lda #230
sta kb::KB_IO + IO::T2CL
stz key_read
stz key_read+1
stz scancode
stz status
; enable SR interrupts
lda #(IO::IRQ::IRQ | IO::IRQ::SR)
sta kb::KB_IO + IO::IER
; load SR to reset
lda kb::KB_IO + IO::SR
rts
.endproc
;;********************************************************************************
;; @function Read the first 8 bits an
;; @modifies: A
;; @details
;; - read shift register
;; - disable shift register interrupts
;; - reset shift register
;; - enable timer 2 interrupts
;; - start timer 2
;; IO::SR has to be read before the next bit is shifted in, which happens ~75us after the irq
;; at 1MHz, handling this interrupt takes about 50us (without any additional debug code),
;; so it should work
;;********************************************************************************
.proc irq_shift_reg_handler
lda kb::KB_IO + IO::SR
sta key_read
stz kb::KB_IO + IO::SR
; disable SR interrupts
lda #IO::IRQ::SR
sta kb::KB_IO + IO::IER
; enable timer interrupts
lda #(IO::IRQ::IRQ | IO::IRQ::T2)
sta kb::KB_IO + IO::IER
; start timer, low order count already in latch after init
lda #0
sta kb::KB_IO + IO::T2CH
rts
.endproc
;;********************************************************************************
;; @function Read the last 3 bits after after timer 2 is up
;; @modifies: A
;; @details
;; - read shift register
;; - disable timer 2 interrupts
;; - enable shift register interrupts
;; - reset shift register
;;********************************************************************************
.proc irq_timer_handler
lda kb::KB_IO + IO::SR
sta key_read + 1
lda kb::KB_IO + IO::T2CL ; clear interrupt flag
; disable timer interrupts
lda #(IO::IRQ::T2)
sta kb::KB_IO + IO::IER
; reset SR
; disabling shifting in acr seems necessary to reset - otherwise
; it continues counting and interrupts after the first 5 bits of the next keypress
lda #(IO::ACR::SR_SIN_PHIE | IO::ACR::T2_IRQ_LOAD)
trb kb::KB_IO + IO::ACR
tsb kb::KB_IO + IO::ACR
stz kb::KB_IO + IO::SR
; enable shift register interrupts
lda #(IO::IRQ::IRQ | IO::IRQ::SR)
sta kb::KB_IO + IO::IER
lda key_read+1
ror
ror
ror
lda key_read ; not affecting carry
rol ; rotate carry into byte, rotate startbit into carry
Reverse A
sta scancode
rts
.endproc
.rodata
.align 256
CHARS_NOMOD:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00"
.byte "\x00\x00\x00\x00\x00q1\x00\x00\x00ysaw2\x00"
.byte "\x00cxde43\x00\x00 vftr5\x00"
.byte "\x00nbhgz6\x00\x00\x00mju78\x00"
.byte "\x00,kio09\x00\x00.-l\xEFp\xE2\x00"
.byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00#\x00\x00"
.byte "\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.align 256
CHARS_MODSHIFT:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDF\x00"
.byte "\x00\x00\x00\x00\x00Q!\x00\x00\x00YSAW\"\x00"
.byte "\x00CXDE$\xED\x00\x00 VFTR%\x00"
.byte "\x00NBHGZ&\x00\x00\x00MJU/(\x00"
.byte "\x00;KIO=)\x00\x00:_L\xEFP\xE2\x00"
.byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00'\x00\x00"
.byte "\x00>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

View File

@ -1,25 +1,17 @@
;;********************************************************************************
;; @module keyboard
;; @type drive
;; @module keyboard_handler
;; @type system
;; @details:
;; Support for a PS2 Keyboard using the shift register of a 6522 VIA
;; Pressing a key causes 11 bits to be sent: 1 start - 8 scancode - 1 parity - 1 stop
;; The VIA is set up to interrupt after 8 bits have been shifted into the shift register
;; from the external clock pulses of the keyboard (additional hardware required to
;; address the hardware bug of the VIA, where bit get lost when the external clock
;; transition happens during falling edge of PHI2). After reading the shift register,
;; the VIAs T2 timer is set to interrupt after the last 3 bits have been shifted in,
;; which takes about ~230ms.
;; This module processes keyboard scancodes from the ps2_keyboard module.
;;********************************************************************************
.ifndef INCLUDE_KEYBOARD
INCLUDE_KEYBOARD = 1
.include "system.h65"
.include "ps2_keyboard.h65"
.scope kb
Import kb,init,irq_shift_reg_handler,irq_timer_handler,scancode,key_read
Import kb, CHARS_NOMOD, CHARS_MODSHIFT
ImportZp kb,status
KB_IO = IO1
.enum STATUS
RELEASE = %10000000
@ -145,7 +137,5 @@ K_RELEASE = $F0
PAGEUP = $7D
.endenum
.endscope
.endif ; guard

View File

@ -0,0 +1,28 @@
.include "keyboard_handler.h65"
Export kb, CHARS_NOMOD, CHARS_MODSHIFT
ExportZp kb,status
.zeropage
status: .res 1
.rodata
.align 256
CHARS_NOMOD:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00"
.byte "\x00\x00\x00\x00\x00q1\x00\x00\x00ysaw2\x00"
.byte "\x00cxde43\x00\x00 vftr5\x00"
.byte "\x00nbhgz6\x00\x00\x00mju78\x00"
.byte "\x00,kio09\x00\x00.-l\xEFp\xE2\x00"
.byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00#\x00\x00"
.byte "\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.align 256
CHARS_MODSHIFT:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDF\x00"
.byte "\x00\x00\x00\x00\x00Q!\x00\x00\x00YSAW\"\x00"
.byte "\x00CXDE$\xED\x00\x00 VFTR%\x00"
.byte "\x00NBHGZ&\x00\x00\x00MJU/(\x00"
.byte "\x00;KIO=)\x00\x00:_L\xEFP\xE2\x00"
.byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00'\x00\x00"
.byte "\x00>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

51
system/ps2_keyboard.h65 Normal file
View File

@ -0,0 +1,51 @@
;;********************************************************************************
;; @module ps2_keyboard
;; @type driver
;; @details:
;; Support for a PS2 Keyboard using the shift register of a 6522 VIA
;; @section Reading a scancode/command answer
;; Pressing a key causes 11 bits to be sent: 1 start - 8 scancode - 1 parity - 1 stop
;; The VIA is set up to interrupt after 8 bits have been shifted into the shift register
;; from the external clock pulses of the keyboard (additional hardware required to
;; address the hardware bug of the VIA, where bit get lost when the external clock
;; transition happens during falling edge of PHI2). After reading the shift register,
;; the VIAs T2 timer is set to interrupt after the last 3 bits have been shifted in,
;; which takes about ~230ms.
;; T2 is used because it leaves to more versatile T1 available for something else
;; @section Processing a scancode
;; The scancode may be processed by storing the address of a handler subroutine in
;; ps2kb::scancode_handler. This handler can load the scancode byte from ps2kb::scancode.
;;
;;
;; To use a different layout, change the enum K and the CHARS_NOMOD and CHARS_MODSHIFT
;; @warning
;; The value with which the timer is loaded must depends on the clock frequency
;; of the keyboard and the computer
;;
;;********************************************************************************
.ifndef INCLUDE_PS2_KEYBOARD
INCLUDE_PS2_KEYBOARD = 1
.include "system.h65"
.scope ps2kb
Import ps2kb,init,begin_receive,send_command,scancode,status,scancode_handler
Import ps2kb, _receive_irq_shift_reg_handler, _receive_irq_timer_handler
VIA = IO1
TIMER = 230 ; 230 ms (@1MHz)
; use RA4 to pull the clock low
CLK_PULL_MASK= %00001000
CLK_PULL_R = IO::RANH
CLK_PULL_DDR = IO::DDRA
.enum STATUS
RECEIVE_KEYS = %10000000
RECEIVE_ANSWER = %01000000
SEND_CMD = %00100000
NONE = %00000000
.endenum
.endscope
.endif ; guard

313
system/ps2_keyboard.s65 Normal file
View File

@ -0,0 +1,313 @@
.include "ps2_keyboard.h65"
.include "string.h65"
.include "lcd.h65"
.include "parity.h65"
Export ps2kb,init,begin_receive,send_command,scancode,status,scancode_handler
Export ps2kb, _receive_irq_shift_reg_handler, _receive_irq_timer_handler
.bss
status: .res 1
send_last_bits: .res 1
send_data: .res 1
send_cmd: .res 1
key_read: .res 2
scancode: .res 1
scancode_handler: .res 2
.code
;;********************************************************************************
;; @macro Enable the clock signal from the keyboard
;; @modifies: A
;; @details
;; Stop pulling the keyboards clock low
;;********************************************************************************
.macro _EnableClock
; set pin to input
lda ps2kb::VIA + ps2kb::CLK_PULL_DDR
and #<~ps2kb::CLK_PULL_MASK
sta ps2kb::VIA + ps2kb::CLK_PULL_DDR
.endmacro
.macro _DisableTimerIRQ
; set pin to input
lda ps2kb::VIA + ps2kb::CLK_PULL_DDR
and #<~ps2kb::CLK_PULL_MASK
sta ps2kb::VIA + ps2kb::CLK_PULL_DDR
.endmacro
;;********************************************************************************
;; @macro Disable the clock signal from the keyboard
;; @modifies: A
;; @details
;; Pulls the keyboards clock low
;;********************************************************************************
.macro _DisableClock
; set pin to output
lda ps2kb::VIA + ps2kb::CLK_PULL_DDR
ora #ps2kb::CLK_PULL_MASK
sta ps2kb::VIA + ps2kb::CLK_PULL_DDR
; set pin low
lda ps2kb::VIA + ps2kb::CLK_PULL_R
and #<~ps2kb::CLK_PULL_MASK
sta ps2kb::VIA + ps2kb::CLK_PULL_R
.endmacro
.proc init
stz key_read
stz key_read+1
stz scancode
stz status
; T2 oneshot mode, clear shift reg
lda ps2kb::VIA + IO::ACR
and #<~IO::ACR_MASK::SR
ora #IO::ACR::T2_IRQ_LOAD
sta ps2kb::VIA + IO::ACR
; the 3 last bits take about 230us, at @1MHz => wait 230 cycles and then the shift register
; (this could be shorter since the it takes a few cycles after the interrupt)
lda #<ps2kb::TIMER
sta ps2kb::VIA + IO::T2CL
; load this rts as scancode_handler
StoreDByte @rts, scancode_handler
@rts:
rts
.endproc
;;********************************************************************************
;; @function Start receiving data from the keyboard
;; @details
;; - use the shift register interrupts to read the first 8 bits
;; - configure timer for timing the read of the last 3 bits
;; @modifies: A
;;********************************************************************************
.proc begin_receive
; disable timer interrupts (this might be called while waiting on the last 3 bits)
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
; set shift register to shift in under external clock on CB1
lda ps2kb::VIA + IO::ACR
and #<~IO::ACR_MASK::SR
ora #IO::ACR::SR_SIN_PHIE
sta ps2kb::VIA + IO::ACR
stz key_read
stz key_read+1
stz scancode
bit status
; set to RECEIVE_KEYS only if RECEIVE_ANSWER is not set
bvs @receive_answer
lda #ps2kb::STATUS::RECEIVE_KEYS
sta status
@receive_answer:
_EnableClock
; todo setup irq handlers
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
; reset and start SR
stz ps2kb::VIA + IO::SR
rts
.endproc
;;********************************************************************************
;; @function Read the first 8 bits an
;; @modifies: A
;; @details
;; - read shift register
;; - disable shift register interrupts
;; - reset shift register
;; - enable timer 2 interrupts
;; - start timer 2
;; IO::SR has to be read before the next bit is shifted in, which happens ~75us after the irq
;; at 1MHz, handling this interrupt takes about 50us (without any additional debug code),
;; so it should work
;;********************************************************************************
.proc _receive_irq_shift_reg_handler
lda ps2kb::VIA + IO::SR
sta key_read
stz ps2kb::VIA + IO::SR
IO_DisableIRQ ps2kb::VIA, IO::IRQ::SR
IO_EnableIRQ ps2kb::VIA, IO::IRQ::T2
; start timer, low order count already in latch after init
lda #>ps2kb::TIMER
sta ps2kb::VIA + IO::T2CH
rts
.endproc
;;********************************************************************************
;; @function Read the last 3 bits after after timer 2 is up
;; @modifies: A
;; @details
;; - read shift register
;; - disable timer 2 interrupts
;; - enable shift register interrupts
;; - reset shift register
;; - jump to the subroutine pointed to by scancode_handler
;;********************************************************************************
.proc _receive_irq_timer_handler
lda ps2kb::VIA + IO::SR
sta key_read + 1
lda ps2kb::VIA + IO::T2CL ; clear interrupt flag
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
; reset SR
; disabling shifting in acr seems necessary to reset - otherwise
; it continues counting and interrupts after the first 5 bits of the next keypress
lda #(IO::ACR::SR_SIN_PHIE | IO::ACR::T2_IRQ_LOAD)
trb ps2kb::VIA + IO::ACR
tsb ps2kb::VIA + IO::ACR
stz ps2kb::VIA + IO::SR
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
lda key_read+1
ror ; stop bit -> C
sta key_read+1 ; parity in bit 0, D7 in bit 1
ror ; parity -> C
ror ; D7 -> C
lda key_read ; not affecting carry
rol ; C -> bit 0, startbit -> C
Reverse A
sta scancode
CalculateOddParity
eor key_read+1 ; if bit 0 is 1 - parity error
and #1 ; bit 1 is still D7
bne @parity_error
; check what to do with the scancode
bit status
bcc @status_not_receive_keys
jmp (scancode_handler)
@status_not_receive_keys:
bvc @status_ignore
jmp _process_cmd_answer
@status_ignore:
rts
@parity_error: ; TODO handle somehow
lda #$ff
sta scancode
DEBUG_LED_ON 2
rts
.endproc
;;********************************************************************************
;; @function Send a command to the keyboard
;; @modifies: A,X,Y
;; @param A: The command byte
;; @param X: The data byte or 0 if only the command byte should be sent
;;********************************************************************************
.proc send_command
sta send_cmd ; store if it needs to be resent
cpx #0
beq @jmp_send_byte
@store_data:
stx send_data
@jmp_send_byte:
jmp _send_byte
.endproc
;;********************************************************************************
;; @function Send a byte to the keyboard
;; @modifies: A,X,Y
;; @param A: The byte to send
;; @details
;; - pull clock low to stop keyboard transmissions
;; - setup shift register to shift out
;; - put startbit + D0-6 in shift register
;; - store D7 + parity + stopbit in memory
;; - TODO setup SR and T2 interrupt handlers
;;********************************************************************************
.proc _send_byte
pha
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
_DisableClock
; (re)set shift register to shift out under external clock on CB1
lda ps2kb::VIA + IO::ACR
and #<~IO::ACR_MASK::SR
ora #IO::ACR::SR_SOUT_PHIE
sta ps2kb::VIA + IO::ACR
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
pla
pha
; split into the 11 bits
CalculateOddParity
tax
pla
clc ; C = 0 (startbi)
rol ; C -> bit 0, D7 -> C
sta ps2kb::VIA + IO::SR
txa
ror ; C -> bit 0 (D7)
ora #%00000100 ; set stopbit
; A = 000001P7 where 7 = D7, P = Parity
sta send_last_bits ; loaded into SR by irq handler
; stop pulling clk low, which causes the startbit to be shifted out
_EnableClock
rts
.endproc
;;********************************************************************************
;; @function Send the lasts 3 bits
;; @modifies: A
;; @details
;; - load the remaining 3 bits of the transmission into the shift register
;; - disable shift register interrupts
;; - enable timer 2 interrupts
;; - start timer 2
;;********************************************************************************
.proc _send_irq_shift_reg_handler
lda send_last_bits
sta ps2kb::VIA + IO::SR
; disable SR interrupts
lda #IO::IRQ::SR
sta ps2kb::VIA + IO::IER
; enable timer interrupts
lda #(IO::IRQ::IRQ | IO::IRQ::T2)
sta ps2kb::VIA + IO::IER
; start timer, low order count already in latch after init
lda #>ps2kb::TIMER
sta ps2kb::VIA + IO::T2CH
rts
.endproc
;;********************************************************************************
;; @function Send databyte or reveice the keyboards answer
;; @modifies: A, X, Y
;; @details
;; - disable timer 2 interrupts
;; - if send_data is zero:
;; - begin_receive
;; - set status RECEIVE_ANSWER
;; - else
;; - send_byte send_data
;; - set send_data = 0
;;********************************************************************************
.proc _send_irq_timer_handler
lda ps2kb::VIA + IO::T2CL ; clear interrupt flag
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
; no SR reset necessary, will be done in begin_receive or send_byte
lda send_data
beq @receive
stz send_data
jmp _send_byte
rts
@receive:
lda #ps2kb::STATUS::RECEIVE_ANSWER
sta status
jmp begin_receive
.endproc
.proc _process_cmd_answer
@success:
rts
.endproc

35
util/parity.h65 Normal file
View File

@ -0,0 +1,35 @@
.ifndef INCLUDE_PARITY
INCLUDE_PARITY = 1
.import count_set_bits
;;********************************************************************************
;; @macro Calculate even parity
;; @modifies: A, X, Y
;; @returns A: 0 - even number of set bits, 1 - uneven number of set bits
;; @returns Y: The number of set bits in the byte
;;********************************************************************************
.macro CalculateEvenParity
jsr count_set_bits
tya
and #1
.endmacro
;;********************************************************************************
;; @macro Calculate odd parity
;; @details
;; Count the number of set bits in Y.
;;
;; @modifies: A, X, Y
;; @returns A: 1 - even number of set bits, 0 - uneven number of set bits
;; @returns Y: The number of set bits in the byte
;;********************************************************************************
.macro CalculateOddParity
jsr count_set_bits
tya
and #1
eor #1
.endmacro
.endif ; guard

23
util/parity.s65 Normal file
View File

@ -0,0 +1,23 @@
.export count_set_bits
;;********************************************************************************
;; @function Initialize the PS2 keyboard
;; @details
;; Count the number of set bits in Y.
;;
;; @modifies: A, X, Y
;; @returns Y: The number of set bits in the byte
;;********************************************************************************
.proc count_set_bits
ldx #8
ldy #0 ; number of set bits
@loop:
dex
beq @done
ror
bcc @loop
@set:
iny
bra @loop
@done:
rts
.endproc

View File

@ -36,6 +36,35 @@ INCLUDE_UTILITY = 1
.endmacro
;;********************************************************************************
;; @macro Store a double byte (immediate) at an address
;; @param addr Address, double byte will be loaded in LE to addr, addr+1
;; @param dbyte Location or value
;; @param reg Register, defaults to A
;; @modifies
;;********************************************************************************
.macro StoreDByte dbyte,addr,reg
; .out .sprintf("%x, %x", dbyte, addr)
.if .blank(reg) .or .match(reg, A)
lda #<dbyte
sta addr
lda #>dbyte
sta addr+1
.elseif .match(reg, X)
ldx #<dbyte
stx addr
ldx #>dbyte
stx addr+1
.elseif .match(reg, Y)
ldy #<dbyte
sty addr
ldy #>dbyte
sty addr+1
.else
.fatal "Invalid reg given to StoreDByte"
.endif
.endmacro
;;********************************************************************************
;; @macro Update a byte in memory using a mask
;; @param addr Address of the byte to update
@ -56,6 +85,7 @@ INCLUDE_UTILITY = 1
.endmacro
;;********************************************************************************
;; @macro Jump to the subroutine and let the routine return at another location
;; @details