Copy Link
Add to Bookmark
Report
Playstation RootCounter Example
ROOTEXMP.ASM
;--------------------------------------------------------------------------
; Here's a little example of how to set up and start a root
; counter and how to use it to play a mod using Silpheed's
; modplayer.
; More info and explanations in various docs on psx.rules.org.
; 1999 | doomed/padua | psx.rules.org | www.padua.org ........
;--------------------------------------------------------------------------
; Addresses
RCnt_Base equ $1f801100 ; Base address for root counters
RCnt_Count equ $0 ; offset for count register
RCnt_Mode equ $4 ; offset for mode register
RCnt_Target equ $8 ; offset for target register
; Counters
DescRC equ $f2000000 ;Rootcounter tag, for event routines.
RCntCNT0 equ $f2000000 ;display pixel
RCntCNT1 equ $f2000001 ;horizontal sync
RCntCNT2 equ $f2000002 ;one-eighth of system clock
RCntCNT3 equ $f2000003 ;vertical sync target value fixed to 1
; Modes
RCntIntr equ $1000 ;Interrupt mode
RCntNotar equ $0100 ;Count to 65535
RCntStop equ $0010 ;Timer stop mode
RCntSC equ $0001 ;System Clock mode
;Mod player routines
MOD_Load equ $80010008 ; Set up the module
MOD_Poll equ $8001031c ; Execute one tick
;Mod player variables
Mod_Channels equ $80011f40 ; # Channels in mod
Mod_LOrder equ $80012150 ; Last Order
Mod_COrder equ $8001216c ; Current Order
Mod_CPattern equ $8001216d ; Current Pattern
Mod_CRow equ $8001216e ; Current Row
Mod_CTick equ $80011271 ; Current Tick
;--------------------------------------------------------------------------
org $80010000
j main
nop
incbin modplay.bin ; The mod player
main
la a0,str1 ; Print welcome line and name of
la a1,Mod ; mod to the console.
jal Printf ;
nop ;
la a0,Mod ; get address of MOD
jal MOD_Load ;
nop ;
la a0,str11
lw a1,Mod_Channels
lw a2,Mod_LOrder
jal Printf
nop
la a0,str2 ; Print that we're setting
jal Printf ; it up to the console.
nop ;
jal ResetEntryInt ; Set up interrupt environment.
nop ;
; Set up root counter.
li a0, RCntCNT1 ; Horizontal clock root counter.
li a1, $00000138 ; 312 lines
li a2, RCntIntr ; Interrupt mode.
jal SetRCnt ; Set the counter
nop ;
li a0, RCntCNT1 ; Horizontal clock root counter.
jal StartRCnt ; Start the interrupt
nop ;
jal EnterCrit ; Open event should be called
nop ; within a critical section..
li a0, RCntCNT1 ; Horizontal clock root counter.
li a1, $00000002 ; Interrupt event.
li a2, $00001000 ; Handle event on interrupt mode
li a3, RHandler ; Address of handling function.
jal OpenEvent ; Create the event.
nop ;
sw v0, rootsav ; Save event number.
jal ExitCrit
nop
la a0,str3 ; Print that we're starting it
jal Printf ; to the console.
nop ;
; And let it roll...
lw a0,rootsav ; get identifier
jal EnableEvent ; turn it on.
nop ;
lpp
la a0,str4 ; And to show it works,
lbu a1,Mod_CRow ; print some info about the
lbu a2,Mod_COrder ; playing mod to the console.
lbu a3,Mod_CPattern
jal Printf
nop
j lpp
nop
;--------------------------------------------------------------------------
; This is the function that gets called when root counter reaches
; target.
RHandler
addiu sp,sp,-$8 ; save ra
sw ra,4(sp)
jal MOD_Poll ; play the mod
nop ;
lw ra,4(sp) ; restore ra
nop
jr ra ; end return
addiu sp,sp,$8 ;
; Because this handler gets called from an event handler,
; there's no need for interrupt handling code.
;--------------------------------------------------------------------------
; System Functions.
;--------------------------------------------------------------------------
; SetRcnt - Sets a root counter. (Root counter 3 can't be set.)
; in: a0: Root counter number.
; a1: Target.
; a2: Mode.
;You can or these modes together:
; RCntStop stops the counter
; RCntIntr sets the counter to interrupt mode.
; RCntSC sets timer speed to system clock.
; RCntNotar sets the timer to count to 65535 instead
; of target.
;--------------------------------------------------------------------------
SetRCnt
ori t2,zero,$b0
jr t2
ori t1,zero,$02
;--------------------------------------------------------------------------
; GetRcnt - Gets current value of a root counter.
; in: a0: Counter number
; out: v0: Current count value.
; Always zero if counter 3(Vsync) is specified.
;--------------------------------------------------------------------------
GetRCnt
ori t2,zero,$b0
jr t2
ori t1,zero,$03
;--------------------------------------------------------------------------
;StartRCnt - Stets a root counter interrupt mask.
;in a0: Root counter
;out v0: 1 for Success
;--------------------------------------------------------------------------
StartRCnt
ori t2,zero,$b0
jr t2
ori t1,zero,$04
;--------------------------------------------------------------------------
; StopRCnt - Clears a root counter interrupt mask.
; In a0: Root counter number.
;--------------------------------------------------------------------------
StopRCnt
ori t2,zero,$b0
jr t2
ori t1,zero,$05
ResetRCnt
ori t2,zero,$b0
jr t2
ori t1,zero,$06
;--------------------------------------------------------------------------
Printf
ori t2,zero,$00a0
jr t2
ori t1,zero,$003f
;--------------------------------------------------------------------------
OpenEvent ; a0 class
addiu t2,zero,$00b0 ; a1 spec
jr t2 ; a2 mode
addiu t1,zero,$0008 ; a3 address of function
nop ; ->v0 #event
;--------------------------------------------------------------------------
EnableEvent
addiu t2,zero,$00b0 ; a0 #event
jr t2
addiu t1,zero,$000c
nop
;--------------------------------------------------------------------------
DisableEvent ; a0 #event
addiu t2,zero,$00b0
jr t2
addiu t1,zero,$000d
nop
;--------------------------------------------------------------------------
CloseEvent ; a0 #event
addiu t2,zero,$00b0
jr t2
addiu t1,zero,$0009
nop
;--------------------------------------------------------------------------
EnterCrit
addiu a0, zero, $0001
syscall $00000
jr ra
nop
;--------------------------------------------------------------------------
ExitCrit
addiu a0, zero, $0002
syscall $00000
jr ra
nop
;--------------------------------------------------------------------------
ResetEntryInt
addiu t2, zero, $00b0
jr t2
addiu t1, zero, $0018
;--------------------------------------------------------------------------
; Data
rootsav dw $0 ; Here goes the event identifier.
str1 db $d,$a,'Root counter example by Doomed/Padua. Modplayer by Silpheed/Hitmen.',$d,$a,$a
db 'Loading MOD: ',$27,'%s',$27,$0d,$0a,0
str11 db '%02d Channels',$2c,' %02d Orders',$0d,$0a,0
str2 db 'Setting up root counter',$0d,$0a,0
str3 db 'Starting root counter',$0d,$0a,$0a,0
str4 db 'Row: %02d',$2c,' Order: %02d',$2c,' Pattern: %02d',$0d,0
align 4 ; always important..
Mod incbin lunatic.hit ; the module
RCOUNT.ASM
;-------------------------------------------------------------
; These are the routines used in the previous example. While
; not exactly the same, they produce the same result and take
; the same arguments as the system calls. They might give you
; some insight in what happens when you call those.
;
; doomed/padua 1999..
;-------------------------------------------------------------
; SetRcnt - Sets a root counter. (Root counter 3 can't be set.)
; in: a0: Root counter number.
; a1: Target.
; a2: Mode.
;You can or these modes together:
; RCntStop stops the counter
; RCntIntr sets the counter to interrupt mode.
; RCntSC sets timer speed to system clock.
; RCntNotar sets the timer to count to 65535 instead
; of target.
;-------------------------------------------------------------
SetRCnt
andi a0,a0,$ffff ; strip descriptor half.
; for kernal compatability.
slti v0,a0,3 ; only values of 0,1 and 2 allowed.
; Vblanc counter can't be set.
bne v0,zero,SRC_Do ; process if true
sll v0,a0,4 ; counter * 16
jr ra ; Else quit and return zero
addu v0,zero,zero ;
SRC_Do
li v1, RCnt_Base ; Find base offset.
addu v0,v1,v0 ;
sw zero,RCnt_Mode(v0) ; Reset Root counter.
andi v1,a2,RCntIntr ; Check for interrupt
sw a1,RCnt_Target(v0) ; Set target.
beq v1,zero,SRC_Noint ;
ori t0,zero,$0040 ; Base mode, IQ2=1
ori t0,t0,$0010 ; Set Irq On.
SRC_Noint
andi v1,a2,RCntNotar ; Check for no target mode
bne v1,zero,SRC_Notar ; flag set?
slti v1,a0,2 ; Check if counters 0 or 1 are targetted.
ori t0,t0,$0008 ; Set count to target on.
SRC_Notar
bne v1,zero,SRC_01 ; handle root counters 0 and 1.
andi v1,a2,RCntSC
; This is only for root counter 2.
bne v1,zero,SRC_SC ; branch if system clock is targetted.
andi v1,a2,RCntStop ;
ori t0,t0,$0200 ; set normal speed
SRC_SC
beq v1,zero,SRC_Nostop ;
nop ;
ori t0,t0,$0001 ; set timer to stop
SRC_Nostop
j SRC_Setmode
nop
; This is for counters 0 and 1.
SRC_01 bne v1,zero,SRC_Setmode ;
nop ;
ori t0,t0,$0100 ; Set normal speed.
SRC_Setmode
sw t0,RCnt_Mode(v0) ; Set the mode and return
jr ra ;
ori v0,zero,1 ;
;-------------------------------------------------------------
; GetRcnt - Gets current value of a root counter.
; in: a0: Counter number
; out: v0: Current count value.
; Always zero if counter 3(Vsync) is specified.
;-------------------------------------------------------------
GetRCnt
andi v1, a0, $ffff ; Strip Root Count Descriptor
slti v0, v1, $0003 ; counter 3(vsync) or improper value?
bne v0, zero, GRC_Do ; no = go.
sll v1, v1, $04 ; Get Counter offset
jr ra ; return 0 and quit.
or v0, zero, zero ;
GRC_Do
li v0, RCnt_Base ;
addu v1, v1, v0 ; Make address
lhu v0, RCnt_Count(v1) ; Get value.
jr ra ; Done.
nop ;
;-------------------------------------------------------------
;StartRCnt - Stets a root counter interrupt mask.
;in a0: Root counter
;out v0: 1 for Success
;-------------------------------------------------------------
StartRCnt
andi v0, a0, $ffff ; Strip Root Count Descriptor
slti v1, v0, $0004 ; root counter 0-3 ?
bne v1,zero,STRC_Do ; yes = handle
sll a0, v0, $02 ; Get table offset (Counter * 4)
jr ra ; else quit & zero
or v0,zero,zero ;
STRC_Do
li v0, $1f801074 ; v0 = address of interrupt mask register.
la v1, RCntIMask ; Get address of Mask table
addu a0, v1, a0 ; add offset
lw a0, $0(a0) ; Get mask value.
lw v1, $0(v0) ; Read mask register
or v1, v1, a0 ; Apply mask
jr ra
sw v1, $0(v0) ; Write to mask register.
;-------------------------------------------------------------
; StopRCnt - Clears a root counter interrupt mask.
; In a0: Root counter number.
;-------------------------------------------------------------
StopRCnt
andi v0, a0, $ffff ; Strip Root Count Descriptor
slti v1, v0, $0004 ; root counter 0-3 ?
bne v1,zero,SPRC_Do ; yes = handle
sll a0, v0, $02 ; Get table offset (Counter * 4)
jr ra ; else quit & zero
or v0,zero,zero ;
SPRC_Do
li v0, $1f801074 ; v0 = address of interrupt mask register.
la v1, RCntIMask ; Get address of Mask table
addu a0, v1, a0 ; add offset
lw a0, $0(a0) ; Get mask value.
lw v1, $0(v0) ; Read mask register
nor a0, zero, a0 ; invert mask value
and v1, v1, a0 ; Apply mask
jr ra
sw v1, $0(v0) ; Write to mask register.
;-------------------------------------------------------------
RCntIMask ; Mask table for interrupt reg.
dw $00000010 ; Root counter 0
dw $00000020 ; Root counter 1
dw $00000040 ; Root counter 2
dw $00000001 ; Root counter 3