Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
386 lines (298 sloc) 13.3 KB
;==================================================================================
; Contents of this file are copyright Phillip Stevens
;
; You have permission to use this for NON COMMERCIAL USE ONLY
; If you wish to use it elsewhere, please include an acknowledgement to myself.
;
; Converted to z88dk z80asm YAZ180 by
; Phillip Stevens @feilipu https://feilipu.me
; August 2017
;
;
; https://github.com/feilipu/
;
; https://feilipu.me/
;
;==============================================================================
;
; INCLUDES SECTION
;
INCLUDE "config_yaz180_private.inc"
;==================================================================================
;
; PUBLIC FUNCTIONS
;
PUBLIC APU_ISR_NMI
PUBLIC APU_INIT, APU_CHK_IDLE
PUBLIC APU_OP_LD, APU_CMD_LD
;==================================================================================
;
; INTERRUPT SECTION
;
; Interrupt Service Routine for the Am9511A-1
;
; Initially called once the required operand pointers and commands are loaded
; Following calls generated by END signal whenever a single APU command is completed
; Sends a new command (with operands if needed) to the APU
SECTION apu_isr ;INTERRUPT SERVICE ORIGIN FOR YAZ180
APU_ISR_NMI:
push af ; store AF, etc, so we don't clobber them
push bc
push de
push hl
ld hl, APUIntCount
inc (hl) ; atomic increment of interrupt entrance count
xor a ; set internal clock = crystal x 1 = 18.432MHz
; that makes the PHI 9.216MHz
out0 (CMR), a ; CPU Clock Multiplier Reg (CMR)
; Am9511A-1 needs TWCS 30ns. This provides 41.7ns.
APU_ISR_ENTRY:
ld a, (APUCMDBufUsed) ; check whether we have a command to do
or a ; zero?
jr z, APU_ISR_END ; if so then clean up and END
ld bc, __IO_APU_PORT_STATUS ; the address of the APU status port in BC
in a, (c) ; read the APU
and __IO_APU_STATUS_ERROR ; any errors?
call nz, APU_ISR_ERROR ; then capture error in APUError
ld hl, (APUCMDOutPtr) ; get the pointer to place where we pop the COMMAND
ld a, (hl) ; get the COMMAND byte
ld (APUStatus), a ; save the COMMAND (as a status byte)
inc l ; move the COMMAND pointer low byte along, 0xFF rollover
ld (APUCMDOutPtr), hl ; write where the next byte should be popped
ld hl, APUCMDBufUsed
dec (hl) ; atomically decrement COMMAND count remaining
and $F0 ; mask most significant nibble of COMMAND
cp __IO_APU_OP_ENT ; check whether it is OPERAND entry COMMAND
jr z, APU_ISR_OP_ENT ; load an OPERAND
cp __IO_APU_OP_REM ; check whether it is OPERAND removal COMMAND
jr z, APU_ISR_OP_REM ; remove an OPERAND
ld a, (APUStatus) ; recover the COMMAND from status byte
ld bc, __IO_APU_PORT_CONTROL ; the address of the APU control port in BC
out (c), a ; load the COMMAND, and do it
APU_ISR_EXIT:
; set internal clock = crystal x 2 = 36.864MHz
ld a, CMR_X2 ; set Hi-Speed flag
out0 (CMR), a ; CPU Clock Multiplier Reg (CMR)
pop hl ; recover HL, etc
pop de
pop bc
pop af
retn
APU_ISR_END: ; we've finished with no errors
xor a
ld (APUStatus), a ; set the COMMAND status to idle (NOP)
jr APU_ISR_EXIT ; we're done here
APU_ISR_OP_ENT:
ld hl, (APUPTROutPtr) ; get the pointer to where we pop OPERAND PTR
ld e, (hl) ; read the OPERAND PTR low byte from the APUPTROutPtr
inc l ; move the POINTER low byte along, 0xFF rollover
ld d, (hl) ; read the OPERAND PTR high byte from the APUPTROutPtr
inc l
ld (APUPTROutPtr), hl ; write where the next POINTER should be read
ld hl, APUPTRBufUsed ; decrement of POINTER count remaining
dec (hl)
dec (hl)
ld bc, __IO_APU_PORT_DATA+$0300 ; the address of the APU data port in BC
ex de, hl ; move the base address of the OPERAND to HL
outi ; output 16 bit OPERAND
ex (sp), hl ; delay for 38 cycles (5us) TWI 1.280us
ex (sp), hl
outi
ld a, (APUStatus) ; recover the COMMAND status
cp __IO_APU_OP_ENT16 ; is it a 2 byte OPERAND
jp z, APU_ISR_ENTRY ; yes? then go back to get another COMMAND
ex (sp), hl ; delay for 38 cycles (5us) TWI 1.280us
ex (sp), hl
outi ; output last two bytes of 32 bit OPERAND
ex (sp), hl ; delay for 38 cycles (5us) TWI 1.280us
ex (sp), hl
outi
jp APU_ISR_ENTRY ; go back to get another COMMAND
APU_ISR_OP_REM:
ld hl, (APUPTROutPtr) ; get the pointer to where we pop OPERAND PTR
ld e, (hl) ; read the OPERAND PTR low byte from the APUPTROutPtr
inc l ; move the POINTER low byte along, 0xFF rollover
ld d, (hl) ; read the OPERAND PTR high byte from the APUPTROutPtr
inc l
ld (APUPTROutPtr), hl ; write where the next POINTER should be read
ld hl, APUPTRBufUsed ; decrement of OPERAND POINTER count remaining
dec (hl)
dec (hl)
ld bc, __IO_APU_PORT_DATA+$0300 ; the address of the APU data port in BC
ex de, hl ; move the base address of the OPERAND to HL
inc hl ; reverse the OPERAND bytes to load
ld a, (APUStatus) ; recover the COMMAND status
cp __IO_APU_OP_REM16 ; is it a 2 byte OPERAND
jr z, APU_ISR_REM16 ; yes then skip over 32bit stuff
inc hl ; increment two more bytes for 32bit OPERAND
inc hl
ind ; get the higher two bytes of 32bit OPERAND
ind
APU_ISR_REM16:
ind ; get 16 bit OPERAND
ind
jp APU_ISR_ENTRY ; go back to get another COMMAND
APU_ISR_ERROR: ; we've an error to notify in A
ld hl, APUError ; collect any previous errors
and (hl) ; and we add any new error types
ld (hl), a ; set the APUError status
ret ; we're done here
;==================================================================================
;
; CODE SECTION
;
;------------------------------------------------------------------------------
; APU_INIT
; Initialises the APU buffers
;
; HL = address of the jump table nmi address
SECTION apu_library ;LIBRARY ORIGIN FOR YAZ180 DURING TESTING
APU_INIT:
push af
push bc
push de
push hl
LD HL, APUCMDBuf ; Initialise COMMAND Buffer
LD (APUCMDInPtr), HL
LD (APUCMDOutPtr), HL
LD HL, APUPTRBuf ; Initialise OPERAND POINTER Buffer
LD (APUPTRInPtr), HL
LD (APUPTROutPtr), HL
XOR A ; clear A register to 0
LD (APUCMDBufUsed), A ; 0 both Buffer counts
LD (APUPTRBufUsed), A
LD (APUCMDBuf), A ; clear COMMAND Buffer
LD HL, APUCMDBuf
LD D, H
LD E, L
INC DE
LD BC, __APU_CMD_SIZE
LDIR
LD (APUPTRBuf), A ; clear OPERAND POINTER Buffer
LD HL, APUPTRBuf
LD D, H
LD E, L
INC DE
LD BC, __APU_PTR_SIZE
LDIR
ld (APUStatus), a ; set APU status to idle (NOP)
ld (APUError), a ; clear APU errors
ld (APUIntCount), a ; set number of interrupts processed to zero
pop hl ; load the jump table nmi address
ld de, APU_ISR_NMI ; load our interrupt origin
; initially there is a RETN there
ld (hl), e ; load the address of the APU NMI jump
inc hl
ld (hl), d
APU_INIT_LOOP:
ld bc, __IO_APU_PORT_STATUS ; the address of the APU status port in bc
in a, (c) ; read the APU
and __IO_APU_STATUS_BUSY ; busy?
jr nz, APU_INIT_LOOP
pop de
pop bc
pop af
ret
;------------------------------------------------------------------------------
; APU_CHK_IDLE
; Confirms whether the APU is idle
; Loop until it returns ready
; Operand Entry and Removal takes little time,
; and we'll be interrupted for Command entry.
; Use after the APU_ISR call.
;
; A = contents of APUError (aggregation of any errors found)
; SCF if no errors
APU_CHK_IDLE:
push bc
APU_CHK_LOOP:
ld a, (APUStatus) ; get the status of the APU
or a ; check it is zero (NOP) command
jr nz, APU_CHK_LOOP ; otherwise wait
ld bc, __IO_APU_PORT_STATUS ; the address of the APU status port in bc
in a, (c) ; read the APU status port
and __IO_APU_STATUS_BUSY ; busy bit set?
jr nz, APU_CHK_LOOP ; yes, then wait
pop bc
ld a, (APUError) ; get the aggregated errors collected
or a
ret nz ; return with no carrry if errors
scf ; set carry flag
ret ; return with APUError in a, carry set if no errors
;------------------------------------------------------------------------------
; APU_OP_LD
; POINTER to OPERAND in DE
; APU COMMAND in A
APU_OP_LD:
push hl ; store HL so we don't clobber it
ld l, a ; store COMMAND so we don't clobber it
ld a, (APUCMDBufUsed) ; Get the number of bytes in the COMMAND buffer
cp __APU_CMD_SIZE-1 ; check whether there is space in the buffer
jr nc, APU_OP_EXIT ; COMMAND buffer full, so exit
ld a, l ; recover the operand entry COMMAND
ld hl, (APUCMDInPtr) ; get the pointer to where we poke
ld (hl), a ; write the COMMAND byte to the APUCMDInPtr
inc l ; move the COMMAND pointer low byte along, 0xFF rollover
ld (APUCMDInPtr), hl ; write where the next byte should be poked
ld hl, APUCMDBufUsed
inc (hl) ; atomic increment of COMMAND count
ld a, (APUPTRBufUsed) ; Get the number of bytes in the OPERAND PTR buffer
cp __APU_PTR_SIZE-2 ; check whether there is space for a OPERAND PTR
jr nc, APU_OP_EXIT ; buffer full, so exit
ld hl, (APUPTRInPtr) ; get the pointer to where we poke
ld (hl), e ; write the low byte of OPERAND PTR to the APUPTRInPtr
inc l ; move the POINTER low byte along, 0xFF rollover
ld (hl), d ; write the high byte of OPERAND PTR to the APUPTRInPtr
inc l
ld (APUPTRInPtr), hl ; write where the next POINTER should be poked
ld hl, APUPTRBufUsed
inc (hl) ; increment of OPERAND PTR count
inc (hl)
APU_OP_EXIT:
pop hl ; recover HL
ret
;------------------------------------------------------------------------------
; APU_CMD_LD
; APU COMMAND in A
APU_CMD_LD:
push hl ; store HL so we don't clobber it
ld l, a ; store COMMAND so we don't clobber it
ld a, (APUCMDBufUsed) ; Get the number of bytes in the COMMAND buffer
cp __APU_CMD_SIZE-1 ; check whether there is space in the buffer
jr nc, APU_CMD_EXIT ; COMMAND buffer full, so exit
ld a, l ; recover the operand entry COMMAND
ld hl, (APUCMDInPtr) ; get the pointer to where we poke
ld (hl), a ; write the COMMAND byte to the APUCMDInPtr
inc l ; move the COMMAND pointer low byte along, 0xFF rollover
ld (APUCMDInPtr), hl ; write where the next byte should be poked
ld hl, APUCMDBufUsed
inc (hl) ; atomic increment of COMMAND count
APU_CMD_EXIT:
pop hl ; recover HL
ret
;==================================================================================
;
; DATA SECTION
;
PUBLIC APUStatus, APUError, APUIntCount
; I/O Buffers must start on 0xnn00 because we increment low byte to roll-over
SECTION apu_data_align_256
APUCMDBuf: DEFS __APU_CMD_SIZE
APUPTRBuf: DEFS __APU_PTR_SIZE
; pad to next 256 byte boundary
;IF (ASMPC & 0xff)
; defs 256 - (ASMPC & 0xff)
;ENDIF
SECTION apu_data
APUCMDInPtr: DEFW APUCMDBuf
APUCMDOutPtr: DEFW APUCMDBuf
APUPTRInPtr: DEFW APUPTRBuf
APUPTROutPtr: DEFW APUPTRBuf
APUCMDBufUsed: DEFB 0
APUPTRBufUsed: DEFB 0
APUStatus: DEFB 0
APUError: DEFB 0
APUIntCount: DEFB 0
;==============================================================================
;
;==============================================================================