Copy Link
Add to Bookmark
Report

Playstation RootCounter Example

PS_2's profile picture
Published in 
Playstation
 · 25 Jun 2021

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

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT