Further IEC coding (switching to assembly)
This commit is contained in:
parent
2650b86c96
commit
01b2765ab0
10
docs/f256k2_registers.txt
Normal file
10
docs/f256k2_registers.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
https://discord.com/channels/691915291721990194/934618943400837130/1266710378612789278:
|
||||
|
||||
$F0_1D00 - $F0_1D1F - SDCARD0
|
||||
$F0_1D20 - $F0_1D3F - SDCARD1 *** This one has moved ***
|
||||
$F0_1D20 - $F0_1D3F - SPLASH LCD (SPI Port)
|
||||
$F0_1D60 - $F0_1D7F - Wiznet Copper SPI Interface
|
||||
$F0_1D80 - $F0_1D9F - Wiznet WIFI UART interface (115K or 2M)
|
||||
$F0_1DA0 - $F0_1DBF - MIDI UART (Fixed @ 31,250Baud)
|
||||
$F0_1DC0 - $F0_1DDF - Master SPI Interface to Supervisor (RP2040)*
|
|
@ -1,85 +0,0 @@
|
|||
;;;
|
||||
;;; Low-level support code for the IEC port
|
||||
;;;
|
||||
|
||||
.public sleep_20us
|
||||
.public sleep_100us
|
||||
.public sleep_300us
|
||||
.public sleep_1ms
|
||||
|
||||
#include "F256/iec_f256.h"
|
||||
|
||||
;
|
||||
; Macros
|
||||
;
|
||||
|
||||
;
|
||||
; Macro to set IEC output pins
|
||||
;
|
||||
; @param pins the bit masks for the pins to set
|
||||
;
|
||||
set_pins: .macro pins
|
||||
lda far:IEC_OUT
|
||||
and #~(\pins)
|
||||
sta far:IEC_OUT
|
||||
.mend
|
||||
|
||||
;
|
||||
; Macro to clear IEC output pins
|
||||
;
|
||||
; @param pins the bit masks for the pins to clear
|
||||
;
|
||||
clr_pins: .macro pins
|
||||
lda far:IEC_OUT
|
||||
ora #(\pins)
|
||||
sta far:IEC_OUT
|
||||
.mend
|
||||
|
||||
;
|
||||
; Macro to set and clear pins in one shot
|
||||
;
|
||||
; @param set_pins the bit masks for the pins to set
|
||||
; @param clr_pins the bit masks for the pins to clear
|
||||
;
|
||||
set_clr_pins: .macro set_pins, clr_pins
|
||||
lda far:IEC_OUT
|
||||
and #~(\set_pins)
|
||||
ora #(\clr_pins)
|
||||
sta far:IEC_OUT
|
||||
.mend
|
||||
|
||||
read_pin: .macro pin
|
||||
pha
|
||||
lda far:IEC_IN
|
||||
bit #(\pin)
|
||||
pla
|
||||
.mend
|
||||
|
||||
;
|
||||
; Routines
|
||||
;
|
||||
|
||||
sleep_20us: phx
|
||||
ldx #20
|
||||
_loop$ dex
|
||||
bne _loop$
|
||||
plx
|
||||
rtl
|
||||
|
||||
sleep_100us: phx
|
||||
ldx #5
|
||||
_loop$ jsl sleep_20us
|
||||
dex
|
||||
bne _loop$
|
||||
plx
|
||||
rtl
|
||||
|
||||
sleep_300us: jsl sleep_100us
|
||||
jsl sleep_100us
|
||||
jsl sleep_100us
|
||||
rtl
|
||||
|
||||
sleep_1ms: jsl sleep_300us
|
||||
jsl sleep_300us
|
||||
jsl sleep_300us
|
||||
jmp sleep_100us
|
662
src/C256/iecll.s
Normal file
662
src/C256/iecll.s
Normal file
|
@ -0,0 +1,662 @@
|
|||
;;;
|
||||
;;; Low-level support code for the IEC port
|
||||
;;;
|
||||
;;; NOTE: routines will be split into private routines and public routines.
|
||||
;;; Private routines will assume near JSRs, 8-bit accumulator and index registers, and interrupts disabled.
|
||||
;;; Public routines will assume far JSRs and 16-bit accumulator and index registers.
|
||||
;;; Public routines will also assume Calypsi calling conventions.
|
||||
;;;
|
||||
|
||||
.public iecll_ioinit
|
||||
.public iecll_in
|
||||
.public iecll_eoi
|
||||
.public iecll_out
|
||||
.public iecll_talk
|
||||
.public iecll_talk_sa
|
||||
.public iecll_untalk
|
||||
.public iecll_listen
|
||||
.public iecll_listen_sa
|
||||
.public iecll_unlisten
|
||||
|
||||
#include "F256/iec_f256.h"
|
||||
|
||||
.section data,data
|
||||
|
||||
eoi_pending: .byte 0
|
||||
rx_eoi: .byte 0
|
||||
delayed: .byte 0
|
||||
queue: .byte 0
|
||||
|
||||
.section code
|
||||
|
||||
;;
|
||||
;; Macros
|
||||
;;
|
||||
|
||||
assert_bit: .macro pin
|
||||
pha
|
||||
lda IEC_OUTPUT_PORT
|
||||
and #(\pin ^ 0xff)
|
||||
sta IEC_OUTPUT_PORT
|
||||
pla
|
||||
rts
|
||||
.endm
|
||||
|
||||
release_bit: .macro pin
|
||||
pha
|
||||
lda IEC_OUTPUT_PORT
|
||||
ora #(\pin)
|
||||
sta IEC_OUTPUT_PORT
|
||||
pla
|
||||
rts
|
||||
.endm
|
||||
|
||||
read_bit: .macro pin
|
||||
pha
|
||||
loop$ lda IEC_INPUT_PORT
|
||||
cmp IEC_INPUT_PORT
|
||||
bne loop$
|
||||
and #(\pin)
|
||||
cmp #1
|
||||
pla
|
||||
rts
|
||||
.endm
|
||||
|
||||
;;
|
||||
;; Private functions
|
||||
;;
|
||||
|
||||
;
|
||||
; Pin accessor functions to assert, release, or read a pin
|
||||
;
|
||||
|
||||
read_SREQ: read_bit IEC_SREQ_i
|
||||
assert_SREQ: assert_bit IEC_SREQ_o
|
||||
release_SREQ: release_bit IEC_SREQ_o
|
||||
|
||||
read_ATN: read_bit IEC_ATN_i
|
||||
assert_ATN: assert_bit IEC_ATN_o
|
||||
release_ATN: release_bit IEC_ATN_o
|
||||
|
||||
read_CLOCK: read_bit IEC_CLK_i
|
||||
assert_CLOCK: assert_bit IEC_CLK_o
|
||||
release_CLOCK: release_bit IEC_CLK_o
|
||||
|
||||
read_DATA: read_bit IEC_DATA_i
|
||||
assert_DATA: assert_bit IEC_DATA_o
|
||||
release_DATA: release_bit IEC_DATA_o
|
||||
|
||||
;;
|
||||
;; Routines to wait various amounts of time
|
||||
;;
|
||||
|
||||
sleep_20us: phx
|
||||
ldx #20
|
||||
_loop$ dex
|
||||
bne _loop$
|
||||
plx
|
||||
rts
|
||||
|
||||
sleep_100us: phx
|
||||
ldx #5
|
||||
_loop$ jsl sleep_20us
|
||||
dex
|
||||
bne _loop$
|
||||
plx
|
||||
rts
|
||||
|
||||
sleep_300us: jsl sleep_100us
|
||||
jsl sleep_100us
|
||||
jsl sleep_100us
|
||||
rts
|
||||
|
||||
sleep_1ms: jsl sleep_300us
|
||||
jsl sleep_300us
|
||||
jsl sleep_300us
|
||||
jmp sleep_100us
|
||||
|
||||
;;
|
||||
;; Code to handle the IEC port
|
||||
;;
|
||||
|
||||
init: jsr sleep_1ms
|
||||
jsr release_ATN
|
||||
jsr release_DATA
|
||||
jsr release_SREQ
|
||||
jsr assert_CLOCK ; IDLE state
|
||||
jsr sleep_1ms
|
||||
jsr sleep_1ms
|
||||
jsr sleep_1ms
|
||||
|
||||
; Bail if ATN and SRQ fail to float back up.
|
||||
; We'll have a more thorough test when we send
|
||||
; our first command.
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
jsr read_SREQ
|
||||
bcc err$
|
||||
jsr read_ATN
|
||||
bcc err$
|
||||
|
||||
jsr sleep_1ms
|
||||
|
||||
clc
|
||||
rts
|
||||
|
||||
err$ sec
|
||||
rts
|
||||
|
||||
;
|
||||
; Send a command byte and release the DATA and CLOCK lines afterwards
|
||||
;
|
||||
; A = the command byte to send
|
||||
;
|
||||
atn_release_data
|
||||
jsr release_DATA
|
||||
jsr release_CLOCK ; TODO: makes /no/ sense; maybe we can remove...
|
||||
jsr atn_common ; NOTE: does NOT release ATN! Does NOT release IRQs!
|
||||
rts
|
||||
|
||||
;
|
||||
; Send a command byte and release the ATN, DATA, and CLOCK lines
|
||||
;
|
||||
; A = the command byte to send
|
||||
;
|
||||
atn_release jsr atn_common
|
||||
|
||||
jsr release_ATN
|
||||
jsr sleep_20us
|
||||
jsr sleep_20us
|
||||
jsr sleep_20us
|
||||
jsr release_CLOCK
|
||||
jsr release_DATA
|
||||
rts
|
||||
|
||||
;
|
||||
; Send a command byte with ATN asserted
|
||||
;
|
||||
; A = the command byte to send
|
||||
;
|
||||
atn_common
|
||||
; Assert ATN; if we aren't already in sending mode,
|
||||
; get there:
|
||||
|
||||
jsr assert_ATN
|
||||
jsr assert_CLOCK
|
||||
jsr release_DATA
|
||||
|
||||
; Now give the devices ~1ms to start listening.
|
||||
jsr sleep_1ms
|
||||
|
||||
; If no one is listening, there's nothing on
|
||||
; the bus, so signal an error.
|
||||
jsr read_DATA
|
||||
bcs err$
|
||||
|
||||
; ATN bytes are technically never EOI bytes
|
||||
stz eoi_pending
|
||||
|
||||
jmp send
|
||||
|
||||
err$
|
||||
; Always release the ATN line on error; TODO: add post delay
|
||||
jmp release_ATN
|
||||
|
||||
;
|
||||
; Send a byte over the IEC bus but mark it as the last byte
|
||||
;
|
||||
; A = the byte to send
|
||||
;
|
||||
send_eoi jsr set_eoi
|
||||
jmp send
|
||||
|
||||
;
|
||||
; Set that the EOI byte is the next to be sent
|
||||
;
|
||||
set_eoi stz eoi_pending
|
||||
dec eoi_pending
|
||||
rts
|
||||
|
||||
;
|
||||
; Sends the queued byte with an EOI
|
||||
;
|
||||
flush bit delayed
|
||||
bpl done$
|
||||
pha
|
||||
lda queue
|
||||
stz delayed
|
||||
jsr send_eoi
|
||||
pla
|
||||
done$: rts
|
||||
|
||||
;
|
||||
; Send a byte over the IEC bus
|
||||
;
|
||||
; A = the byte to send
|
||||
;
|
||||
send
|
||||
; Assumes we are in the sending state:
|
||||
; the host is asserting CLOCK and the devices are asserting DATA.
|
||||
|
||||
; There must be at least 100us between bytes.
|
||||
jsr sleep_300us
|
||||
|
||||
; ; Clever cheating (PJW: removed since we disable interrupts for all this code)
|
||||
|
||||
; ; Act as an ersatz listener to keep the other listeners busy
|
||||
; ; until we are ready to receive. This is NOT part of the
|
||||
; ; IEC protocol -- we are doing this in lieu of an interrupt.
|
||||
; jsr assert_DATA
|
||||
|
||||
; Release CLOCK to signal that we are ready to send
|
||||
; We can do this without disabling interrupts because
|
||||
; we are also asserting DATA.
|
||||
jsr release_CLOCK
|
||||
|
||||
; Now we wait for all of the listeners to acknowledge.
|
||||
wait$
|
||||
jsr sleep_20us
|
||||
|
||||
; Check to see if all listeners have acknowledged
|
||||
jsr read_DATA
|
||||
bcs ready$
|
||||
|
||||
; Other listeners are still busy; go back to sleep.
|
||||
bra wait$
|
||||
|
||||
ready$ bit eoi_pending
|
||||
bpl send$
|
||||
|
||||
eoi$
|
||||
; Alas, we can't get too clever here, or the 1541 hates us.
|
||||
|
||||
; Hard-wait the 200us for the drive to acknowledge the EOI.
|
||||
; This duration is technically unbounded, but the ersatz
|
||||
; listener trick during the EOI signal, and the drive
|
||||
; already had the opportunity to delay before starting the
|
||||
; ack, so hopefully it will stay in nominal 250us range.
|
||||
|
||||
TYE$ jsr read_DATA
|
||||
bcs TYE$
|
||||
|
||||
; Now we're basically back to the point where we are waiting
|
||||
; for Rx ack. The trick does work here.
|
||||
|
||||
; Clear the eoi minus flag, so our next send will be data.
|
||||
lsr eoi_pending
|
||||
|
||||
; The drive should hold DATA for at least 60us. Give it
|
||||
; 20us, and then repeat our ersatz listener trick.
|
||||
jsr sleep_20us
|
||||
; jsr assert_DATA (PJW: removed trick)
|
||||
bra wait$
|
||||
|
||||
send$
|
||||
; Give the listeners time to notice that the've all ack'd
|
||||
jsr sleep_20us ; NOT on the C64
|
||||
|
||||
; Now start pushing out the bits. Note that the timing
|
||||
; is not critical, but each clock state must last at
|
||||
; least 20us (with 70us more typical for clock low).
|
||||
|
||||
phx
|
||||
ldx #8
|
||||
loop$
|
||||
; TODO: opt test for a frame error
|
||||
|
||||
; Clock out the next bit
|
||||
jsr assert_CLOCK
|
||||
jsr sleep_20us
|
||||
lsr a
|
||||
bcs one$
|
||||
|
||||
zero$ jsr assert_DATA
|
||||
bra clock$
|
||||
|
||||
one$ jsr release_DATA
|
||||
|
||||
clock$
|
||||
; Toggle the clock
|
||||
jsr sleep_20us ; TODO: Maybe extend this.
|
||||
|
||||
jsr sleep_20us ; 1541 needs this.
|
||||
jsr release_CLOCK
|
||||
|
||||
jsr sleep_20us
|
||||
dex
|
||||
bne loop$
|
||||
plx
|
||||
|
||||
; Finish the last bit and wait for the listeners to ack.
|
||||
jsr release_DATA
|
||||
jsr assert_CLOCK
|
||||
|
||||
; Now wait for listener ack. Of course, if there are
|
||||
; multiple listeners, we can only know that one ack'd.
|
||||
; This can take up to a millisecond, so another good
|
||||
; candidate for a kernel thread or interrupt.
|
||||
|
||||
; TODO: ATN release timing appears to be semi-critical; we may need
|
||||
; to completely change the code below.
|
||||
|
||||
ack$ jsr read_DATA
|
||||
bcs ack$
|
||||
clc
|
||||
rts
|
||||
|
||||
;
|
||||
; Wait for a byte of data to be read from the IEC port
|
||||
;
|
||||
recv_data
|
||||
|
||||
; Assume not EOI until proved otherwise
|
||||
stz rx_eoi
|
||||
|
||||
; Wait for the sender to have a byte
|
||||
wait1$ jsr read_CLOCK
|
||||
bcc wait1$
|
||||
|
||||
; TODO: start and check a timer
|
||||
|
||||
; Signal we are ready to receive
|
||||
ready$ jsr release_DATA
|
||||
|
||||
; Wait for all other listeners to signal
|
||||
wait2$ jsr read_DATA
|
||||
bcc wait2$
|
||||
|
||||
; Wait for the first bit or an EOI condition
|
||||
; Each iteration takes 6-7us
|
||||
lda #0 ; counter
|
||||
wait3$ inc a
|
||||
beq eoi$
|
||||
jsr read_CLOCK
|
||||
bcc recv$
|
||||
adc #7 ; microseconds per loop
|
||||
bcc wait3$
|
||||
|
||||
eoi$ lda rx_eoi
|
||||
bmi error$
|
||||
|
||||
; Ack the EOI
|
||||
jsr assert_DATA
|
||||
jsr sleep_20us
|
||||
jsr sleep_20us
|
||||
jsr sleep_20us
|
||||
|
||||
; Set the EOI flag.
|
||||
dec rx_eoi ; TODO: error on second round
|
||||
|
||||
; Go back to the ready state
|
||||
bra ready$
|
||||
|
||||
error$ sec
|
||||
rts
|
||||
|
||||
recv$
|
||||
; Clock in the bits
|
||||
phx
|
||||
ldx #8
|
||||
|
||||
wait_fall$ jsr read_CLOCK
|
||||
bcs wait_fall$
|
||||
|
||||
wait_rise$ jsr read_CLOCK
|
||||
bcc wait_rise$
|
||||
|
||||
jsr read_DATA
|
||||
ror a
|
||||
dex
|
||||
bne wait_fall$
|
||||
plx
|
||||
|
||||
; Ack
|
||||
jsr sleep_20us
|
||||
jsr assert_DATA
|
||||
|
||||
; Drives /usually/ work with a lot less, but
|
||||
; I see failures on the SD2IEC on a status check
|
||||
; after file-not-found when debugging is turned off.
|
||||
jsr sleep_20us ; Seems to be missing the ack.
|
||||
jsr sleep_20us ; Seems to be missing the ack.
|
||||
jsr sleep_20us ; Seems to be missing the ack.
|
||||
jsr sleep_20us ; Seems to be missing the ack.
|
||||
|
||||
; Return EOI in NV
|
||||
clc
|
||||
bit rx_eoi
|
||||
ora #0
|
||||
rts
|
||||
|
||||
;;
|
||||
;; Public Functions
|
||||
;;
|
||||
|
||||
;
|
||||
; Initialize the IEC interface
|
||||
;
|
||||
; extern short iecll_ioinit()
|
||||
;
|
||||
; Returns 0 on success, -1 if no devices found
|
||||
;
|
||||
iecll_ioinit php
|
||||
sei ; Disable interrupts
|
||||
sep #0x30 ; Switch to 8-bit registers
|
||||
|
||||
stz delayed
|
||||
|
||||
jsr init
|
||||
bcs err$
|
||||
|
||||
plp
|
||||
lda ##0
|
||||
rtl
|
||||
|
||||
err$ plp
|
||||
lda ##0xffff
|
||||
rtl
|
||||
|
||||
;
|
||||
; Send a TALK command to a device
|
||||
;
|
||||
; extern short iecll_talk(uint8_t device)
|
||||
;
|
||||
; A = the number of the device to become the talker
|
||||
;
|
||||
iecll_talk php
|
||||
sei
|
||||
sep #0x30
|
||||
|
||||
ora #0x40
|
||||
jsr flush
|
||||
jsr atn_release_data ; NOTE: does NOT drop ATN!
|
||||
|
||||
plp
|
||||
rtl
|
||||
|
||||
;
|
||||
; Send the secondary address to the TALK command, release ATN, and turn around control of the bus
|
||||
;
|
||||
; extern short iecll_talk_sa(uint8_t secondary_address)
|
||||
;
|
||||
; A = the secondary address to send
|
||||
;
|
||||
iecll_talk_sa php
|
||||
sei
|
||||
sep #0x30
|
||||
|
||||
jsr atn_common
|
||||
|
||||
jsr assert_DATA
|
||||
jsr release_ATN
|
||||
jsr release_CLOCK
|
||||
1$ jsr read_CLOCK
|
||||
bcs 1$ ; TODO: should time out.
|
||||
|
||||
plp
|
||||
rtl
|
||||
|
||||
;
|
||||
; Send a LISTEN command to a device
|
||||
;
|
||||
; extern short iecll_listen(uint8_t device)
|
||||
;
|
||||
; A = the number of the device to become the listener
|
||||
;
|
||||
iecll_listen php
|
||||
sei
|
||||
sep #0x30
|
||||
|
||||
ora #0x20
|
||||
jsr flush
|
||||
jsr atn_release_data ; NOTE: does NOT drop ATN!
|
||||
|
||||
plp
|
||||
rtl
|
||||
|
||||
;
|
||||
; Send the secondary address to the LISTEN command and release ATN
|
||||
;
|
||||
; extern short iecll_listen_sa(uint8_t secondary_address)
|
||||
;
|
||||
; A = the secondary address to send
|
||||
;
|
||||
iecll_listen_sa php
|
||||
sei
|
||||
sep #0x30
|
||||
|
||||
jsr atn_common
|
||||
jsr release_ATN
|
||||
|
||||
; TODO: WARNING! No delay here!
|
||||
; TODO: IMHO, should wait at least 100us to avoid accidental turn-around!
|
||||
; TODO: tho we do protect against this in the send code.
|
||||
|
||||
plp
|
||||
rtl
|
||||
|
||||
;
|
||||
; Send the UNTALK command to all devices and drop ATN
|
||||
;
|
||||
; extern void iecll_untalk()
|
||||
;
|
||||
iecll_untalk php
|
||||
sei
|
||||
sep #0x30
|
||||
|
||||
; Detangled from C64 sources; TODO: compare with Stef's
|
||||
|
||||
lda #0x5f
|
||||
|
||||
; There should never be a need to flush here, and if you
|
||||
; do manage to call IECOUT between a TALK/TALKSA and an
|
||||
; UNTALK, the C64 will flush it while ATN is asserted and
|
||||
; the drive will be mighty confused.
|
||||
;
|
||||
; TODO: track the state and cause calls to IECOUT to fail.
|
||||
|
||||
; pre-sets CLOCK IMMEDIATELY before the ATN ... again, TODO: makes no sense
|
||||
jsr assert_CLOCK
|
||||
jsr atn_release
|
||||
|
||||
plp
|
||||
rtl
|
||||
|
||||
;
|
||||
; Send the UNLISTEN command to all devices
|
||||
;
|
||||
; extern void iecll_unlisten()
|
||||
;
|
||||
iecll_unlisten php
|
||||
sei
|
||||
sep #0x30
|
||||
|
||||
; Detangled from C64 sources; TODO: compare with Stef's
|
||||
|
||||
lda #0x3f
|
||||
jsr flush
|
||||
jsr atn_release
|
||||
|
||||
plp
|
||||
rtl
|
||||
|
||||
;
|
||||
; Try to get a byte from the IEC bus
|
||||
;
|
||||
; NOTE: EOI flag is set, if this character is the last to be read from the active talker.
|
||||
;
|
||||
; extern uint8_t iecll_in()
|
||||
;
|
||||
; Returns:
|
||||
; A = the byte read
|
||||
;
|
||||
iecll_in php
|
||||
sei ; Disable interrupts
|
||||
sep #0x30 ; Switch to 8-bit registers
|
||||
|
||||
jsr recv_data
|
||||
|
||||
; NOTE: we'll just read from the eoi variable in a separate function
|
||||
; We might need to return it here folded in with the data somwhow
|
||||
|
||||
plp
|
||||
and ##0x00ff
|
||||
rtl
|
||||
|
||||
;
|
||||
; Check to see if the last byte read was an EOI byte
|
||||
;
|
||||
; extern short iecll_eoi()
|
||||
;
|
||||
; Returns:
|
||||
; A = 0 if not EOI, -1 if EOI
|
||||
;
|
||||
iecll_eoi php
|
||||
sep #0x30
|
||||
|
||||
lda rx_eoi
|
||||
beq not_eoi$
|
||||
|
||||
plp
|
||||
lda ##0xffff
|
||||
rtl
|
||||
|
||||
not_eoi$ plp
|
||||
lda #0
|
||||
rtl
|
||||
|
||||
;
|
||||
; Send a byte to the IEC bus. Actually sends the previous byte and queues the current byte.
|
||||
;
|
||||
; extern void iecll_out(uint8_t byte)
|
||||
;
|
||||
; Inputs:
|
||||
; A = the byte to send
|
||||
;
|
||||
iecll_out php
|
||||
sei
|
||||
sep #0x30
|
||||
|
||||
; Sends the byte in A.
|
||||
; Actually, sends the previous IECOUT byte, and queues
|
||||
; this one for later transmission. This is done to
|
||||
; ensure that we can mark the last data byte with an EOI.
|
||||
|
||||
clc
|
||||
bit delayed
|
||||
bpl queue$
|
||||
|
||||
; Send the old byte
|
||||
pha
|
||||
lda queue
|
||||
jsr send
|
||||
pla
|
||||
stz delayed
|
||||
|
||||
; Queue the new byte
|
||||
queue$ sta queue
|
||||
dec delayed
|
||||
|
||||
plp
|
||||
rtl
|
|
@ -1,7 +1,7 @@
|
|||
# VPATH=.:../../module/Calypsi-remote-debug/src
|
||||
DEBUGGER=../module/Calypsi-remote-debug/src
|
||||
|
||||
UNIT := F256K
|
||||
UNIT := F256
|
||||
MEMORY := RAM
|
||||
|
||||
# Define OS-dependent variables
|
||||
|
@ -40,7 +40,7 @@ else ifeq ($(UNIT),C256_FMX)
|
|||
else ifeq ($(UNIT),F256)
|
||||
CPU=w65816
|
||||
C_SRCS_DEBUGGER=$(DEBUGGER)/agent.c $(DEBUGGER)/c256-uart.c $(DEBUGGER)/low_level_WDC65816.s
|
||||
SRCS_FOR_UNIT=C256/jumptable.s C256/io_stubs.c C256/extras.s
|
||||
SRCS_FOR_UNIT=C256/jumptable.s C256/io_stubs.c C256/extras.s C256/iecll.s
|
||||
CFLAGS_FOR_UNIT=-DMODEL=2 -DCPU=255 --code-model large --data-model large
|
||||
|
||||
ifeq ($(MEMORY),ROM)
|
||||
|
@ -51,7 +51,7 @@ else ifeq ($(UNIT),F256)
|
|||
else ifeq ($(UNIT),F256K)
|
||||
CPU=w65816
|
||||
C_SRCS_DEBUGGER=$(DEBUGGER)/agent.c $(DEBUGGER)/c256-uart.c $(DEBUGGER)/low_level_WDC65816.s
|
||||
SRCS_FOR_UNIT=C256/jumptable.s C256/io_stubs.c C256/extras.s
|
||||
SRCS_FOR_UNIT=C256/jumptable.s C256/io_stubs.c C256/extras.s C256/iecll.s
|
||||
CFLAGS_FOR_UNIT=-DMODEL=2 -DCPU=255 --code-model large --data-model large
|
||||
|
||||
ifeq ($(MEMORY),ROM)
|
||||
|
|
|
@ -37,14 +37,14 @@ else ifeq ($(UNIT),F256)
|
|||
AS=as65816
|
||||
AR=nlib
|
||||
|
||||
SRCS_FOR_UNIT=txt_f256.c kbd_f256.c kbd_f256jr.c indicators_c256.c interrupts_f256.c sdc_f256.c # timers_c256.c
|
||||
SRCS_FOR_UNIT=txt_f256.c kbd_f256.c kbd_f256jr.c indicators_c256.c interrupts_f256.c sdc_f256.c iec.c # timers_c256.c
|
||||
CFLAGS_FOR_UNIT=-DMODEL=2 -DCPU=255 --code-model large --data-model large # --target Foenix
|
||||
else ifeq ($(UNIT),F256K)
|
||||
CC=cc65816
|
||||
AS=as65816
|
||||
AR=nlib
|
||||
|
||||
SRCS_FOR_UNIT=txt_f256.c kbd_f256.c kbd_f256k.c indicators_c256.c interrupts_f256.c sdc_f256.c # timers_c256.c
|
||||
SRCS_FOR_UNIT=txt_f256.c kbd_f256.c kbd_f256k.c indicators_c256.c interrupts_f256.c sdc_f256.c iec.c # timers_c256.c
|
||||
CFLAGS_FOR_UNIT=-DMODEL=2 -DCPU=255 --code-model large --data-model large # --target Foenix
|
||||
endif
|
||||
|
||||
|
|
75
src/dev/iec.c
Normal file
75
src/dev/iec.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @file iec_port.c
|
||||
* @author your name (you@domain.com)
|
||||
* @brief Implement the low level control code for the IEC port on the F256
|
||||
* @version 0.1
|
||||
* @date 2024-07-19
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "iecll.h"
|
||||
#include "dev/txt_screen.h"
|
||||
|
||||
short iec_open(uint8_t device, uint8_t channel, bool is_write, char * command) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void iec_close(uint8_t device, uint8_t channel) {
|
||||
iecll_listen(device);
|
||||
iecll_listen_sa(0xe0 | (channel & 0x0f));
|
||||
iecll_unlisten();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the raw status string from an IEC device
|
||||
*
|
||||
* @param device number of the IEC device to query
|
||||
* @param buffer character buffer in which to write the status
|
||||
* @param count the maximum number of bytes to fetch
|
||||
* @return the number of bytes read from the device
|
||||
*/
|
||||
short iec_status(uint8_t device, char * buffer, short count) {
|
||||
short i = 0;
|
||||
|
||||
// Zero out the buffer
|
||||
memset((void *)buffer, 0, count);
|
||||
|
||||
// Open the device, channel 15
|
||||
txt_put(0, '-');
|
||||
iecll_talk(device);
|
||||
txt_put(0, '.');
|
||||
iecll_talk_sa(0x6f);
|
||||
txt_put(0, '.');
|
||||
|
||||
// Copy the status bytes
|
||||
for (i = 0; i < count; i++) {
|
||||
uint8_t byte = iecll_in();
|
||||
txt_put(0, (char)byte);
|
||||
buffer[i] = (char)byte;
|
||||
if (iecll_eoi()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer[i+1] = 0;
|
||||
|
||||
iecll_untalk();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the IEC port
|
||||
*
|
||||
* @return short 0 on success, a negative number indicates an error
|
||||
*/
|
||||
short iec_init() {
|
||||
short result = iecll_ioinit();
|
||||
|
||||
return result;
|
||||
}
|
|
@ -9,8 +9,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef __iec_port_h__
|
||||
#define __iec_port_h__
|
||||
#ifndef __iec_h__
|
||||
#define __iec_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -20,8 +20,9 @@
|
|||
* @param device number of the IEC device to query
|
||||
* @param buffer character buffer in which to write the status
|
||||
* @param count the maximum number of bytes to fetch
|
||||
* @return the number of bytes read from the device
|
||||
*/
|
||||
extern void iec_status(uint8_t device, char * buffer, short count);
|
||||
extern short iec_status(uint8_t device, char * buffer, short count);
|
||||
|
||||
/**
|
||||
* @brief Initialize the IEC port
|
|
@ -1,307 +0,0 @@
|
|||
/**
|
||||
* @file iec_port.c
|
||||
* @author your name (you@domain.com)
|
||||
* @brief Implement the low level control code for the IEC port on the F256
|
||||
* @version 0.1
|
||||
* @date 2024-07-19
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <calypsi/intrinsics65816.h>
|
||||
|
||||
#include "F256/iec_f256.h"
|
||||
#include "errors.h"
|
||||
|
||||
/**
|
||||
* @brief Wait for 20 microseconds
|
||||
*
|
||||
*/
|
||||
static void sleep_20us() {
|
||||
__asm(
|
||||
" phx"
|
||||
" ldx ##20"
|
||||
"1$ dex"
|
||||
" bne 1$"
|
||||
" plx"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for 100 microseconds
|
||||
*
|
||||
*/
|
||||
static void sleep_100us() {
|
||||
__asm(
|
||||
" phx"
|
||||
" ldx #5"
|
||||
"1$ jsl sleep_20us"
|
||||
" dex"
|
||||
" bne 1$"
|
||||
" plx"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for 300 microseconds
|
||||
*
|
||||
*/
|
||||
static void sleep_300us() {
|
||||
sleep_100us();
|
||||
sleep_100us();
|
||||
sleep_100us();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for 1 millisecond
|
||||
*
|
||||
*/
|
||||
static void sleep_1ms() {
|
||||
sleep_300us();
|
||||
sleep_300us();
|
||||
sleep_300us();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assert pins on the IEC port
|
||||
*
|
||||
* @param pins mask bits of the pins to assert
|
||||
*/
|
||||
static void iec_assert(uint8_t pins) {
|
||||
*IEC_OUT &= ~pins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Release pins on the IEC port
|
||||
*
|
||||
* @param pins mask bits of the pins to release
|
||||
*/
|
||||
static void iec_release(uint8_t pins) {
|
||||
*IEC_OUT |= pins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assert and release pins in one transaction
|
||||
*
|
||||
* @param assert_pins mask bits of pins to assert
|
||||
* @param release_pins mask bits of pins to release
|
||||
*/
|
||||
static void iec_assert_release(uint8_t assert_pins, uint8_t release_pins) {
|
||||
*IEC_OUT = (*IEC_OUT & ~assert_pins) | release_pins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a pin on the IEC port, given its mask bit
|
||||
*
|
||||
* @param pin mask bit of the pin to read
|
||||
* @return uint8_t 0 = pin released, 1 = pin asserted
|
||||
*/
|
||||
static uint8_t iec_read(uint8_t pin) {
|
||||
uint8_t v1 = *IEC_IN;
|
||||
uint8_t v2 = *IEC_IN;
|
||||
while (v1 != v2) {
|
||||
v1 = v2;
|
||||
v2 = *IEC_IN;
|
||||
}
|
||||
return ((v2 & pin == 0) ? 1 : 0);
|
||||
}
|
||||
|
||||
static short iec_send_common(uint8_t data, bool eoi) {
|
||||
sleep_300ms();
|
||||
|
||||
iec_release(IEC_PIN_CLK | IEC_PIN_DATA);
|
||||
|
||||
// Wait for the DATA line to be released by all the listeners
|
||||
// NOTE: we may need to use Gadget's clever DATA hack
|
||||
do {
|
||||
sleep_20us();
|
||||
} while(iec_read(IEC_PIN_DATA));
|
||||
|
||||
if (eoi) {
|
||||
// If EOI, we wait until the listener ACKs the EOI
|
||||
while (!iec_read(IEC_PIN_DATA)) ;
|
||||
|
||||
// Thgen wait for the DATA
|
||||
do {
|
||||
sleep_20us();
|
||||
} while(iec_read(IEC_PIN_DATA));
|
||||
}
|
||||
|
||||
// Actually send the bits...
|
||||
|
||||
sleep_20us();
|
||||
for (short bit = 0; bit < 8; bit++) {
|
||||
iec_assert(IEC_PIN_CLOCK);
|
||||
if (data & 0x01) {
|
||||
iec_assert(IEC_PIN_DATA);
|
||||
} else {
|
||||
iec_release(IEC_PIN_DATA);
|
||||
}
|
||||
data = data >> 1;
|
||||
|
||||
sleep_20us();
|
||||
sleep_20us();
|
||||
iec_release(IEC_PIN_CLOCK);
|
||||
sleep_20us();
|
||||
}
|
||||
|
||||
iec_release(IEC_PIN_DATA);
|
||||
iec_assert(IEC_PIN_CLK);
|
||||
|
||||
for (short count = 0; count < 50; count++) {
|
||||
if (iec_read(IEC_PIN_DATA)) {
|
||||
break;
|
||||
}
|
||||
sleep_20us();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static short iec_atn_common(uint8_t data) {
|
||||
__interrupt_state_t state = __get_interrupt_state();
|
||||
__disable_interrupts;
|
||||
|
||||
iec_assert_release((IEC_PIN_ATN | IEC_PIN_CLK), IEC_PIN_DATA);
|
||||
sleep_1ms();
|
||||
|
||||
if (iec_read(IEC_PIN_DATA)) {
|
||||
iec_release(IEC_PIN_ATN);
|
||||
__restore_interrupt_state(state);
|
||||
return ERR_GENERAL;
|
||||
}
|
||||
|
||||
short result = iec_send_common(data, false);
|
||||
__restore_interrupt_state(state);
|
||||
return result;
|
||||
}
|
||||
|
||||
static short iec_atn_release(uint8_t data) {
|
||||
iec_atn_common(data);
|
||||
iec_release(IEC_PIN_ATN);
|
||||
sleep_20us();
|
||||
sleep_20us();
|
||||
sleep_20us();
|
||||
iec_release(IEC_PIN_CLK | IEC_PIN_DATA);
|
||||
}
|
||||
|
||||
static uint8_t iec_read_b() {
|
||||
bool is_eoi = false;
|
||||
while(iec_read(IEC_PIN_CLK)) {
|
||||
// TODO: detect EOI here
|
||||
;
|
||||
}
|
||||
|
||||
iec_release(IEC_PIN_DATA);
|
||||
|
||||
while(iec_read(IEC_PIN_CLK)) ;
|
||||
|
||||
uint8_t data = 0;
|
||||
for (short count = 0; count < 8; count++) {
|
||||
data = data >> 1;
|
||||
|
||||
while(iec_read(IEC_PIN_CLK)) ;
|
||||
|
||||
if (iec_read(IEC_PIN_DATA)) {
|
||||
data |= 0x80;
|
||||
}
|
||||
|
||||
while(!iec_read(IEC_PIN_CLK)) ;
|
||||
}
|
||||
|
||||
iec_assert(IEC_PIN_DATA);
|
||||
sleep_20us();
|
||||
iec_release(IEC_PIN_DATA);
|
||||
|
||||
if (eoi) {
|
||||
// We have the last byte, wait for the talker to turn around the bus
|
||||
while (iec_read(IEC_PIN_CLK)) ;
|
||||
|
||||
// Take back control of the bus
|
||||
iec_assert(IEC_PIN_CLK);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the TALK command to an IEC device
|
||||
*
|
||||
* @param device the number of the IEC device to make a talker
|
||||
*/
|
||||
void iec_talk(uint8_t device) {
|
||||
return iec_atn_common(IEC_CMD_TALK | (device & 0x0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the secondary address for the talk command
|
||||
*
|
||||
* @param channel the channel for the secondary address
|
||||
*/
|
||||
static void iec_talk_sa(uint8_t channel) {
|
||||
iec_atn_release_release(channel & 0x0f);
|
||||
|
||||
// Turn around control of the bus to the talker
|
||||
iec_assert(IEC_PIN_DATA);
|
||||
iec_release(IEC_PIN_CLK);
|
||||
|
||||
// Wait for acknowledgement that we have a talker
|
||||
while(!iec_read(IEC_PIN_CLK)) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the UNTALK command
|
||||
*
|
||||
*/
|
||||
void iec_untalk() {
|
||||
iec_atn_release(IEC_CMD_UNTALK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the UNLISTEN command
|
||||
*
|
||||
*/
|
||||
static void iec_unlisten() {
|
||||
iec_atn_release(IEC_CMD_UNLISTEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the raw status string from an IEC device
|
||||
*
|
||||
* @param device number of the IEC device to query
|
||||
* @param buffer character buffer in which to write the status
|
||||
* @param count the maximum number of bytes to fetch
|
||||
*/
|
||||
void iec_status(uint8_t device, char * buffer, short count) {
|
||||
iec_talk(device);
|
||||
iec_talk_sa(IEC_CMD_OPENCH | 0x0f);
|
||||
|
||||
// TODO: handle the EOI
|
||||
|
||||
for (short i = 0; i < count; i++) {
|
||||
char c = iec_read_b();
|
||||
buffer[i] = c;
|
||||
}
|
||||
|
||||
iec_untalk();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the IEC port
|
||||
*
|
||||
* @return short 0 on success, a negative number indicates an error
|
||||
*/
|
||||
short iec_init() {
|
||||
iec_assert_release(IEC_PIN_CLK, (IEC_PIN_ATN | IEC_PIN_DATA | IEC_PIN_SREQ));
|
||||
sleep_1ms();
|
||||
sleep_1ms();
|
||||
sleep_1ms();
|
||||
|
||||
if (iec_read(IEC_PIN_ATN) || iec_read(IEC_PIN_SREQ)) {
|
||||
return ERR_NOT_READY;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -5,8 +5,6 @@
|
|||
#ifndef __iec_f256_h__
|
||||
#define __iec_f256_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define IEC_CMD_LISTEN 0x20
|
||||
#define IEC_CMD_TALK 0x40
|
||||
#define IEC_CMD_OPENCH 0x60
|
||||
|
@ -15,14 +13,17 @@
|
|||
#define IEC_CMD_UNLISTEN 0x3F
|
||||
#define IEC_CMD_UNTALK 0x5F
|
||||
|
||||
#define IEC_IN ((volatile uint8_t *)0xf01680)
|
||||
#define IEC_OUT ((volatile uint8_t *)0xf01681)
|
||||
#define IEC_INPUT_PORT 0xf01680
|
||||
#define IEC_DATA_i 0x01
|
||||
#define IEC_CLK_i 0x02
|
||||
#define IEC_ATN_i 0x10
|
||||
#define IEC_SREQ_i 0x80
|
||||
|
||||
#define IEC_PIN_DATA 0x01
|
||||
#define IEC_PIN_CLK 0x02
|
||||
#define IEC_PIN_ATN 0x10
|
||||
#define IEC_PIN_SREQ 0x80
|
||||
|
||||
// TODO: fill out with the actual registers
|
||||
#define IEC_OUTPUT_PORT 0xf01681
|
||||
#define IEC_DATA_o 0x01
|
||||
#define IEC_CLK_o 0x02
|
||||
#define IEC_ATN_o 0x10
|
||||
#define IEC_RST_o 0x40
|
||||
#define IEC_SREQ_o 0x80
|
||||
|
||||
#endif
|
|
@ -44,6 +44,7 @@
|
|||
#elif MODEL == MODEL_FOENIX_F256KE
|
||||
#define HAS_EXTERNAL_SIDS 1
|
||||
#define HAS_OPL3 1
|
||||
#define HAS_IEC 1
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -89,4 +90,8 @@
|
|||
#define HAS_SNES_GAMEPAD 0
|
||||
#endif
|
||||
|
||||
#ifndef HAS_IEC
|
||||
#define HAS_IEC 0
|
||||
#endif
|
||||
|
||||
#endif
|
91
src/include/iecll.h
Normal file
91
src/include/iecll.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* @file ieecll.h
|
||||
* @author your name (you@domain.com)
|
||||
* @brief Declare the functions that provide low-level access to the IEC port
|
||||
* @version 0.1
|
||||
* @date 2024-07-27
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __iecll_h__
|
||||
#define __iecll_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize the IEC interface
|
||||
*
|
||||
* @return short 0 on success, -1 if no devices found
|
||||
*/
|
||||
extern short iecll_ioinit();
|
||||
|
||||
/**
|
||||
* @brief Send a TALK command to a device
|
||||
*
|
||||
* @param device the number of the device to become the talker
|
||||
* @return short
|
||||
*/
|
||||
extern short iecll_talk(uint8_t device);
|
||||
|
||||
/**
|
||||
* @brief Send the secondary address to the TALK command, release ATN, and turn around control of the bus
|
||||
*
|
||||
* @param secondary_address the secondary address to send
|
||||
* @return short
|
||||
*/
|
||||
extern short iecll_talk_sa(uint8_t secondary_address);
|
||||
|
||||
/**
|
||||
* @brief Send a LISTEN command to a device
|
||||
*
|
||||
* @param device
|
||||
* @return short the number of the device to become the listener
|
||||
*/
|
||||
extern short iecll_listen(uint8_t device);
|
||||
|
||||
/**
|
||||
* @brief Send the secondary address to the LISTEN command and release ATN
|
||||
*
|
||||
* @param secondary_address the secondary address to send
|
||||
* @return short
|
||||
*/
|
||||
extern short iecll_listen_sa(uint8_t secondary_address);
|
||||
|
||||
/**
|
||||
* @brief Send the UNTALK command to all devices and drop ATN
|
||||
*
|
||||
*/
|
||||
extern void iecll_untalk();
|
||||
|
||||
/**
|
||||
* @brief Send the UNLISTEN command to all devices
|
||||
*
|
||||
*/
|
||||
extern void iecll_unlisten();
|
||||
|
||||
/**
|
||||
* @brief Try to get a byte from the IEC bus
|
||||
*
|
||||
* NOTE: EOI flag is set, if this character is the last to be read from the active talker.
|
||||
*
|
||||
* @return uint8_t the byte read
|
||||
*/
|
||||
extern uint8_t iecll_in();
|
||||
|
||||
/**
|
||||
* @brief Check to see if the last byte read was an EOI byte
|
||||
*
|
||||
* @return short 0 if not EOI, any other number if EOI
|
||||
*/
|
||||
extern short iecll_eoi();
|
||||
|
||||
/**
|
||||
* @brief Send a byte to the IEC bus. Actually sends the previous byte and queues the current byte.
|
||||
*
|
||||
* @param byte the byte to send
|
||||
*/
|
||||
extern void iecll_out(uint8_t byte);
|
||||
|
||||
#endif
|
|
@ -50,11 +50,11 @@
|
|||
#include "dev/dma.h"
|
||||
#include "dev/fdc.h"
|
||||
#include "dev/fsys.h"
|
||||
#include "dev/iec.h"
|
||||
#include "dev/ps2.h"
|
||||
#include "dev/rtc.h"
|
||||
#include "dev/sdc.h"
|
||||
#include "dev/txt_screen.h"
|
||||
|
||||
#include "dev/uart.h"
|
||||
#include "snd/codec.h"
|
||||
#include "snd/psg.h"
|
||||
|
@ -176,6 +176,10 @@ void initialize() {
|
|||
INFO("Console installed.");
|
||||
}
|
||||
|
||||
#if HAS_IEC
|
||||
iec_init();
|
||||
#endif
|
||||
|
||||
/* Initialize the timers the MCP uses */
|
||||
timers_init();
|
||||
INFO("Timers initialized");
|
||||
|
@ -482,10 +486,14 @@ int main(int argc, char * argv[]) {
|
|||
|
||||
test_sysinfo();
|
||||
// test_psg();
|
||||
test_kbd();
|
||||
// test_kbd();
|
||||
long jiffies = timers_jiffies();
|
||||
printf("Jiffies: %ld\n", jiffies);
|
||||
|
||||
printf("Attempting to get status for IEC drive #8: ");
|
||||
short n = iec_status(8, message, 256);
|
||||
printf("\"%s\"\n", message);
|
||||
|
||||
// Attempt to start up the user code
|
||||
// log(LOG_INFO, "Looking for user startup code:");
|
||||
// boot_launch();
|
||||
|
|
Loading…
Reference in a new issue