DEVICE ZXSPECTRUM128
include "gsports.asm"
include "gscodedefs.asm"
include "vs10xx.asm"
XTALI_FREQ = 14000000 ;14mhz on NGS
CLOCKF_VS1011 = 0x8000|((XTALI_FREQ+1000)/2000) ;28mhz
CLOCKF_VS1033 = ((XTALI_FREQ-8000000+2000)/4000)|SC_MULT_03_35X|SC_ADD_03_00X ;49mhz
CLOCKF_VS1053 = ((XTALI_FREQ-8000000+2000)/4000)|SC_MULT_53_40X|SC_ADD_53_00X ;56mhz
MACRO WDC
; in a,(SSTAT)
; and M_MCRDY
; jr z,$-4
;TODO: why MCRDY polling works fine on real hardware, but not in UnrealSpeccy?
;Neo Player Light does this
call noper
ENDM
MACRO WDD
in a,(SSTAT)
rrca
jr nc,$-3
ENDM
MACRO ATOZX
out (ZXDATWR),a
in a,(ZXSTAT)
rlca
jr c,$-3
ENDM
MACRO CALCFREEBUFFERSPACE
ld a,h
sub d
and 0x7f
ENDM
MACRO LOAD256 IENABLED
.loadloop
in a,(ZXSTAT) ;check if data or command is pending
and M_CBIT|M_DBIT
jr z,.loadloop
rlca
jr c,.loaddata
;got command
IF IENABLED
di
ENDIF
call processcommand
IF IENABLED
ei
ENDIF
jr .loadloop
.loaddata
in a,(ZXDATRD)
ld (de),a
inc e
jr nz,.loadloop
inc d
ENDM
org GSPROGSTART
begin di
ld sp,GSSTACKADDR
call checkifngs
;uploading is done via interrupt handler
ld hl,interrupthandler
ld (GSINTERRUPTTABLEENTRYADDR),hl
ld a,GSINTERRUPTTABLEENTRYADDR>>8
ld i,a
im 2
call mutemod
ld a,%10011100
out (SCTRL),a
;set ngs to 10mhz
ld d,C_10MHZ
call ngssetfreq
;hw decoder reset
ld a,M_MPXRS
out (SCTRL),a
WDC
;clear reset signal
ld a,M_MPXRS|M_SETNCLR
out (SCTRL),a
;go to 12mhz
ld d,C_12MHZ
call ngssetfreq
;write to a register after reset
ld l,SCI_VOL
ld de,SV_SILENCE
call vswriteregister
;read chip id
ld l,SCI_STATUS
call vsreadregister
ld a,e
and SS_VER_MASK
ld (vsversion),a
;set an arbitrary writable page for the ring buffer in 0x8000...0xffff
ld a,2
out (MPAG),a
startnewstream
call vssoftreset ;reset decoder setting SDI to compatibility mode
;start preloading
ld h,0x70
ld de,0x8000 ;de is ring buffer write pointer
preloadloop
LOAD256 0
ld a,d
cp 0xdf
jr c,preloadloop
;start uploading
ld hl,0x8000 ;hl is ring buffer read pointer
ei
mainloop
in a,(ZXSTAT) ;check if a command is pending
rrca
jr nc,checkifcandownload
;handle command
di
call processcommand
ei
jr mainloop
checkifcandownload
CALCFREEBUFFERSPACE
cp 3
jr c,mainloop ;keep read and write addresses of the ring buffer at least 256 bytes apart
LOAD256 1
set 7,d
jr mainloop
interrupthandler
push af
push bc
ld a,d
sub h
and 0x7f
cp 3
jr c,skipupload ;keep read and write addresses of the ring buffer at least 256 bytes apart
in a,(SSTAT)
rrca
jr nc,skipupload ;check if data requested
ld b,32 ;upload 32 bytes
uploadloop
ld a,(hl)
out (MD_SEND),a
inc hl
djnz uploadloop
set 7,h ;make sure the address didn't wrap under 0x8000
;blink LED
; ld a,h
; rlca
; rlca
; out (LEDCTR),a
skipupload
pop bc
pop af
ei
ret
vssoftreset
ld l,SCI_VOL
ld de,SV_SILENCE
call vswriteregister ;set volume to minimum
ld l,SCI_MODE
call vsreadregister
ld a,e
xor SM_RESET
ld e,a
ld a,d
and ~(SM_SDINEW>>8) ;NGS implements compatibility mode only
ld d,a
call vswriteregister ;reset
ld a,e
xor SM_RESET
ld e,a
call vswriteregister ;clear reset
volumevalue=$+1
ld de,0
ld l,SCI_VOL
call vswriteregister ;restore volume
call vsclockvalue
ld l,SCI_CLOCKF
call vswriteregister ;set internal clock
vsversion=$+1
ld a,255
cp SS_VER_VS1001
ret nz
ld l,0x02 ;Force the clock-doubler on by writing 0x8008 to SCI_INT_FCTLH.
ld de,0x8008 ;The datasheet states that you should never write to this register.
jr vswriteregister ;This is, however, an exception.
;l = register
;de = value
vswriteregister
WDD
WDC
ld a,M_MCNCS
out (SCTRL),a ;SCI start
WDC
ld bc,MC_SEND
ld a,2
out (c),a ;write op
WDC
out (c),l ;which register
WDC
out (c),d ;high byte
WDC
out (c),e ;low byte
WDC
ld a,M_MCNCS|M_SETNCLR
out (SCTRL),a ;SCI end
WDC
ret
;l = register
;out: de = value
vsreadregister
WDD
WDC
ld a,M_MCNCS
out (SCTRL),a ;SCI start
WDC
ld bc,MC_SEND
ld a,3
out (c),a ;read op
WDC
out (c),l ;which register
WDC
ld a,0xff
out (c),a
WDC
ld bc,MC_READ
in d,(c) ;high byte
WDC
ld a,0xff
out (MC_SEND),a
WDC
in e,(c) ;low byte
WDC
ld a,M_MCNCS|M_SETNCLR
out (SCTRL),a ;SCI end
WDC
ret
vsclockvalue
;out: de = CLOCKF value
ld a,(vsversion)
ld de,CLOCKF_VS1011
cp SS_VER_VS1001
ret z
cp SS_VER_VS1002
ret z
cp SS_VER_VS1011
ret z
ld de,CLOCKF_VS1033
cp SS_VER_VS1003
ret z
cp SS_VER_VS1033
ret z
ld de,CLOCKF_VS1053
ret
ngssetfreq
;d = frequency constant
in a,(GSCFG0)
and %11001111
or d
out (GSCFG0),a
ret
checkifngs
in a,(GSCFG0)
cp 255
ret nz
.msgloop
in a,(ZXSTAT)
rrca
call c,processcommand
jr .msgloop
mutemod
xor a
out (VOL1),a
out (VOL2),a
out (VOL3),a
out (VOL4),a
out (VOL5),a
out (VOL6),a
out (VOL7),a
out (VOL8),a
ret
processcommand
in a,(ZXCMD)
cp CMDCOUNT
jr nc,cmdreset ;received an invalid command, so the player crashed?
rlca
ld (commandtable+1),a
out (CLRCBIT),a
commandtable
jr $
jr cmdreset : assert CMDRESET==0
jr cmdgetfreebufferspace : assert CMDGETFREEBUFFERSPACE==1
jr cmdgetchipid : assert CMDGETCHIPID==2
jr cmdrestartstream : assert CMDRESTARTSTREAM==3
jr cmdvolumeup : assert CMDVOLUMEUP==4
jr cmdvolumedown : assert CMDVOLUMEDOWN==5
cmdreset
ld a,(vsversion)
inc a
jp z,0
call vssoftreset
ld d,C_20MHZ
call ngssetfreq
jp 0
cmdgetfreebufferspace
CALCFREEBUFFERSPACE
ATOZX
ret
cmdgetchipid
ld a,(vsversion)
ATOZX
ret
cmdrestartstream
ld sp,GSSTACKADDR
jp startnewstream
cmdvolumeup
ld a,(volumevalue)
or a
ret z
dec a
setvolume
push hl
push de
push bc
ld e,a
ld d,a
ld (volumevalue),de
ld l,SCI_VOL
call vswriteregister
pop bc
pop de
pop hl
ret
cmdvolumedown
ld a,(volumevalue)
cp SV_SILENCE>>8
ret nc
inc a
jr setvolume
noper
ds 18,0
ret
end
savebin "gscode.bin",begin,end-begin