aboutsummaryrefslogtreecommitdiff
path: root/examples/system/timer/main.c
blob: c424e84f575a0a803aa1651cfa90639bd294f195 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
 * PSn00bSDK hardware timer example
 * (C) 2023 spicyjpeg - MPL licensed
 */

#include <stdint.h>
#include <psxgpu.h>
#include <psxetc.h>
#include <psxapi.h>
#include <hwregs_c.h>

/* Display/GPU context utilities */

#define SCREEN_XRES 320
#define SCREEN_YRES 240

#define BGCOLOR_R 48
#define BGCOLOR_G 24
#define BGCOLOR_B  0

typedef struct {
	DISPENV disp;
	DRAWENV draw;
} Framebuffer;

typedef struct {
	Framebuffer db[2];
	int         db_active;
} RenderContext;

void init_context(RenderContext *ctx) {
	Framebuffer *db;

	ResetGraph(0);
	ctx->db_active = 0;

	db = &(ctx->db[0]);
	SetDefDispEnv(&(db->disp),           0, 0, SCREEN_XRES, SCREEN_YRES);
	SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
	setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
	db->draw.isbg = 1;
	db->draw.dtd  = 1;

	db = &(ctx->db[1]);
	SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES);
	SetDefDrawEnv(&(db->draw),           0, 0, SCREEN_XRES, SCREEN_YRES);
	setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
	db->draw.isbg = 1;
	db->draw.dtd  = 1;

	PutDrawEnv(&(db->draw));
	//PutDispEnv(&(db->disp));

	// Create a text stream at the top of the screen.
	FntLoad(960, 0);
	FntOpen(8, 16, 304, 208, 2, 512);
}

void display(RenderContext *ctx) {
	Framebuffer *db;

	DrawSync(0);
	VSync(0);
	ctx->db_active ^= 1;

	db = &(ctx->db[ctx->db_active]);
	PutDrawEnv(&(db->draw));
	PutDispEnv(&(db->disp));
	SetDispMask(1);
}

/* Interrupt handlers */

typedef struct {
	int irq_count, last_irq_count, irqs_per_sec;
} TimerState;

static volatile TimerState timer_state[3];

static void timer0_handler(void) {
	timer_state[0].irq_count++;
}

static void timer1_handler(void) {
	timer_state[1].irq_count++;
}

static void timer2_handler(void) {
	timer_state[2].irq_count++;
}

static void vblank_handler(void) {
	int refresh_rate = (GetVideoMode() == MODE_PAL) ? 50 : 60;

	// Only update once per second (every 50 or 60 vblanks).
	if (VSync(-1) % refresh_rate)
		return;

	for (int i = 0; i < 3; i++) {
		TimerState *state = &timer_state[i];

		int count             = state->irq_count;
		state->irqs_per_sec   = count - state->last_irq_count;
		state->last_irq_count = count;
	}
}

/* Main */

static RenderContext ctx;

int main(int argc, const char* argv[]) {
	init_context(&ctx);

	// Set up the timers and register callbacks for their interrupts.
	TIMER_CTRL(0) = 0x0160; // Dotclock input, repeated IRQ on overflow
	TIMER_CTRL(1) = 0x0160; // Hblank input, repeated IRQ on overflow
	TIMER_CTRL(2) = 0x0260; // CLK/8 input, repeated IRQ on overflow

	__builtin_memset(timer_state, 0, sizeof(timer_state));

	EnterCriticalSection();
	InterruptCallback(IRQ_TIMER0, &timer0_handler);
	InterruptCallback(IRQ_TIMER1, &timer1_handler);
	InterruptCallback(IRQ_TIMER2, &timer2_handler);
	VSyncCallback(&vblank_handler);
	ExitCriticalSection();

	while (1) {
		FntPrint(-1, "HARDWARE TIMER EXAMPLE\n\n");

		for (int i = 0; i < 3; i++) {
			TimerState *state = &timer_state[i];

			FntPrint(-1, "TIMER %d:\n", i);
			FntPrint(-1, " VALUE:  %d\n", TIMER_VALUE(i));
			FntPrint(-1, " IRQS:   %d\n", state->irq_count);
			FntPrint(-1, " IRQS/S: %d\n\n", state->irqs_per_sec);
		}

		FntPrint(-1, "VBLANK COUNTER:\n");
		FntPrint(-1, " VALUE:  %d\n\n", VSync(-1));

		FntFlush(-1);
		display(&ctx);
	}

	return 0;
}