Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ;;;;;;;;;;;;;;;;;;;;;;;;
- ; NEStronic Code for the NES CPU (RP2A03)
- ;;;;;;;;;;;;;;;;;;;;;;;;
- .import __STACK_START__, __STACK_SIZE__
- .include "zeropage.inc"
- .include "nes.inc"
- ;
- ; Definitions
- ;
- ; PCA9564 Registers
- PCA_STA = $6000 ; Status (R)
- PCA_TO = $6000 ; Time-out (W)
- PCA_DAT = $6001 ; Data (R/W)
- PCA_ADR = $6002 ; Own address (R/W)
- PCA_CON = $6003 ; Control (R/W)
- ; PCA9564 Clock speeds
- PCA_CON_330kHz = $00
- PCA_CON_288kHz = $01
- PCA_CON_217kHz = $02
- PCA_CON_146kHz = $03
- PCA_CON_88kHz = $04
- PCA_CON_59kHz = $05
- PCA_CON_44kHz = $06
- PCA_CON_36kHz = $07
- ; PCA9564 Control flags
- PCA_CON_AA = $80 ; Assert Acknowledge
- PCA_CON_ENSIO = $40 ; Enable
- PCA_CON_STA = $20 ; Start
- PCA_CON_STO = $10 ; Stop
- PCA_CON_SI = $08 ; Serial Interrupt
- PCA_CON_CR = $07 ; Clock Rate (MASK)
- ; I2C Commands
- CMD_OUT = $16 ; Output pin control
- CMD_APU = $40 ; APU register write
- .segment "ZEROPAGE"
- ; Variables go here
- cmd_len: .res 1
- cmd_buf: .res 7
- .segment "STARTUP"
- .segment "CODE"
- start:
- sei ; Ignore IRQs
- cld ; Disable decimal mode
- ; Setup the stack
- ldx #$FF
- txs
- ; Initialize the APU
- jsr init_apu
- ; Wait a little bit
- ldy #20
- jsr delay_y_frames
- jsr i2c_init
- @cmd_loop:
- jsr i2c_slave_cmd
- lda cmd_len ; Check the command length
- cmp #0
- beq @cmd_loop ; Loop around if zero
- lda cmd_buf ; Check the first command byte
- cmp #CMD_OUT ; Output pin control
- beq @out_cmd
- cmp #CMD_APU ; APU register write
- beq @apu_cmd
- jmp @cmd_loop
- @out_cmd:
- lda cmd_len
- cmp #2 ; Check for 1 argument
- bne @cmd_loop
- ldx #1 ; Index the argument
- lda cmd_buf, X ; Load the argument
- sta $4016 ; Store the argument in the OUT register
- jmp @cmd_loop
- @apu_cmd:
- lda cmd_len
- cmp #3 ; Check for 2 arguments
- bne @cmd_loop
- ldx #1 ; Index the register argument
- ldy cmd_buf, X ; Load the register argument into Y
- ldx #2 ; Index the value argument
- lda cmd_buf, X ; Load the data argument into A
- sta $4000, Y ; Store A in $4000+Y
- jmp @cmd_loop
- forever:
- jmp forever ; loop forever to halt
- ;
- ; Initializes APU registers and silences all channels
- ; Note: This will stomp on $4016, which can cause issues for our testing
- ;
- init_apu:
- lda #$0F
- sta $4015
- ldy #0
- : lda @regs,y
- sta $4000,y
- iny
- cpy #$18
- bne :-
- rts
- @regs:
- .byte $30,$7F,$00,$00
- .byte $30,$7F,$00,$00
- .byte $80,$00,$00,$00
- .byte $30,$00,$00,$00
- .byte $00,$00,$00,$00
- .byte $00,$0F,$00,$C0
- ;
- ; Initialize the PCA9564 I2C Controller
- ;
- i2c_init:
- lda #$FF
- sta PCA_TO ; Set timeout
- lda #($08 << 1)
- sta PCA_ADR ; Set own address to $08
- lda #(PCA_CON_ENSIO | PCA_CON_330kHz)
- sta PCA_CON ; Enable serial I/O
- nop ; Wait for oscillator startup
- nop
- lda #(PCA_CON_AA | PCA_CON_ENSIO | PCA_CON_330kHz)
- sta PCA_CON ; Start as slave
- rts
- ;
- ; Wait for the I2C interrupt flag to be set
- ;
- i2c_wait_busy:
- ; TODO: store a timeout counter somewhere
- @loop:
- lda PCA_CON ; Load the current value of I2CCON
- and #PCA_CON_SI ; Mask the SI bit
- cmp #PCA_CON_SI ; Check if the SI bit is set
- ; TODO: decrement a timer counter. and fail if zero
- bne @loop ; If not set, then loop
- rts
- ;
- ; Wait as an I2C slave for the next command
- ;
- i2c_slave_cmd:
- ldx #$00
- stx cmd_len ; Zero out the command length
- jsr i2c_wait_busy ; Wait for SI
- lda PCA_STA
- cmp #$60 ; Own SLA+W has been received
- beq @receiver
- cmp #$A8 ; Own SLA+R has been received
- beq @transmitter
- jmp @fault
- @receiver:
- lda #(PCA_CON_AA | PCA_CON_ENSIO | PCA_CON_330kHz)
- sta PCA_CON ; Reset SI bit
- jsr i2c_wait_busy
- lda PCA_STA
- cmp #$80 ; Data has been received
- bne @receiver_done
- lda PCA_DAT
- sta cmd_buf, X ; Store the data byte
- inx ; Increment the length counter
- cpx #8 ; Check for the max command length
- beq @fault ; Fault if we received too many bytes
- jmp @receiver
- @receiver_done:
- lda PCA_STA
- cmp #$A0 ; A STOP condition has been received
- bne @fault
- stx cmd_len ; Store the command length
- jmp @done
- @transmitter:
- ; TODO: Mode not yet supported, this is placeholder code
- lda #$FF
- sta PCA_DAT ; Load dummy data
- lda #(PCA_CON_AA | PCA_CON_ENSIO | PCA_CON_330kHz)
- sta PCA_CON ; Reset SI bit
- jsr i2c_wait_busy
- lda PCA_STA
- cmp #$B8 ; Data byte in I2CDAT has been transmitted (ACK)
- ; TODO: Loop to @transmitter and send next byte
- cmp #$C0 ; Data byte in I2CDAT has been transmitted (NACK)
- bne @fault
- @transmitter_done:
- jmp @done
- @fault:
- ; figure out what to do in a fault condition
- @done:
- lda #(PCA_CON_AA | PCA_CON_ENSIO | PCA_CON_330kHz)
- sta PCA_CON ; Reset SI bit
- rts
- ;
- ; Delays Y/60 second
- ;
- delay_y_frames:
- : jsr delay_frame
- dey
- bne :-
- rts
- ;
- ; Delays 1/60 second
- ;
- delay_frame:
- ; delay 29816
- lda #67
- : pha
- lda #86
- sec
- : sbc #1
- bne :-
- pla
- sbc #1
- bne :--
- rts
- ;
- ; Handle interrupts by doing nothing
- ;
- nmi:
- irq:
- rti
- .segment "RODATA"
- ; Static data goes here
- .segment "VECTORS"
- .word nmi ;$FFFA NMI
- .word start ;$FFFC Reset
- .word irq ;$FFFE IRQ
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement