Optimizing 82C54 emulated timertick?

I've written a script in c/c++ for emulating a timer tick of a 82C54 chip (PIC).
However, even after optimizing it it still runs slow (currently being called at ~1.19MHz, so twice this (~2.38MHz function calls) for input signal low/high.

Anyone knows why it runs so slow (I've already changed the code to use jumptables in cases that may run many times (HW82C54_STATUS datatype is a handler (with syntax: void AHandler(byte channel); ).

BTW the executable runs at a PSP (333MHz processor, single core afaik).

Part 1/2
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
inline byte modeblockhi(byte mode) //Mode blocks when hi input?
{
	return (mode==0)?1:0; //Only block with mode 0!
}

//Assume mode 2=6 and 3=7, mode 2/6 is equal to 3/7 except 3/7 decrements by 2 only when gate is high, none else.

HW82C54_STATUS status_82C54_handler[0x10000]; //All status handlers (word size)

inline byte getOperatingMode(byte channel) //Get a operating mode!
{
	return ((Timer82C54.operatingmode[channel]&(0x3<<1))>>1); //What operating mode?
}

/*

Start of Status Handlers (Timer)

*/


void status_init(byte channel)
{
	Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORRELOADSET; //All channels wait for the reload to be set!
}

void status_waitforreloadsetorcommandset(byte channel) //Wait for reload or command to be set!
{
	if ((Timer82C54.channelstatus[channel]&CHANNELSTATUS_RELOADSET) || (Timer82C54.channelstatus[channel]&CHANNELSTATUS_COMMANDSET)) //Reload or command set?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_WAITFORRELOADSETORCOMMANDSET; //Turn of reload/command wait!
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_RELOADSET; //Turn off reload set!
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_COMMANDSET; //Turn off command set!
	}
}

/*

for waitforreloadset!

*/

void reloadset0(byte channel)
{
	if (Timer82C54.signalrising==0) //Dropped?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_RELOADSET; //Turn off reload set!
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_RUNNING; //Start running: we're loaded, so run!
		Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Load the reload value!
		setOutput(channel,0); //Set output low!
	}
	else
	{
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORRELOADSET; //Try again later!
	}
}

void reloadset1(byte channel)
{
	if (Timer82C54.signalrising==0) //Dropped?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_RELOADSET; //Turn off reload set!
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_RUNNING; //Start running: we're loaded, so run!
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORGATEHIGH; //Wait for rising edge of gate!
		setOutput(channel,0); //Set output low!
	}
	else
	{
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORRELOADSET; //Try again later!
	}
}

void reloadset26(byte channel)
{
	if (Timer82C54.signalrising==0) //Dropped?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_RELOADSET; //Turn off reload set!
		Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Load the reload value!
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_RUNNING; //Start running: we're loaded, so run!
	}
	else
	{
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORRELOADSET; //Try again later!
	}
}

void reloadset37(byte channel)
{
	if (Timer82C54.signalrising==0) //Dropped?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_RELOADSET; //Turn off reload set!
		Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Load the reload value!
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_RUNNING; //Start running: we're loaded, so run!
	}
	else
	{
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORRELOADSET; //Try again later!
	}
}

void reloadset4(byte channel)
{
	if (Timer82C54.signalrising==0) //Dropped?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_RELOADSET; //Turn off reload set!
		Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Load the reload value!
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_RUNNING; //Start running: we're loaded, so run!
	}
	else
	{
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORRELOADSET; //Try again later!
	}
}

void reloadset5(byte channel)
{
	if (gatestatus(channel)==1) //Rised?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_RELOADSET; //Turn off reload set!
		Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Load the reload value!
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_RUNNING; //Start running: we're loaded, so run!
	}
	else
	{
		Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORRELOADSET; //Try again later!
	}
}

HW82C54_STATUS status_waitforreloadset_handlers[8] = {
	reloadset0,
	reloadset1,
	reloadset26,
	reloadset37,
	reloadset4,
	reloadset5,
	reloadset26,
	reloadset37
	}; //All handlers for WAITFORRELOADSET!

void status_waitforreloadset(byte channel) //Wait for reload to be set!
{
	//Waiting for load/reset!
	if (Timer82C54.channelstatus[channel]&CHANNELSTATUS_RELOADSET) //Reload set?
	{
		Timer82C54.readbackstatus[channel] &= ~0x40; //We're loading the reload register!
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_WAITFORRELOADSET; //Turn of reload wait!
		status_waitforreloadset_handlers[getOperatingMode(channel)](channel); //Execute handler for the reload!
	}
}

/*

Rest of the handlers!

*/

void status_waitforgatehigh(byte channel) //Wait for gate to become high!
{
	if (gatestatus(channel)==1) //Gotten high?
	{
		//Do something!
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_WAITFORGATEHIGH; //We've gotten high, turn option off!
		switch (getOperatingMode(channel)) //What operating mode?
		{
			case 1:
				setOutput(channel,0); //Set output low!
				Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Load the reload value!
				break;
			case 2:
			case 6:
				Timer82C54.channelstatus[channel] |= CHANNELSTATUS_RUNNING; //Start running!
				Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Load the reload value!
				break;
			case 3:
			case 7:
				Timer82C54.channelstatus[channel] |= CHANNELSTATUS_RUNNING; //Start running!
				Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Load the reload value!
				break;
			default: //Unknown?
				break; //Do nothing!
		}
	}
}

void status_waitforgatelow(byte channel) //Wait for gate to become low!
{
	if (!gatestatus(channel)) //Gotten low?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_WAITFORGATELOW; //We've gotten low, turn option off!
	}
}

void status_skipcycle(byte channel) //Skip a cycle!
{
	if (getOperatingMode(channel)==4 || getOperatingMode(channel)==5) //Mode 4/5?
	{
		if (Timer82C54.signalrising==Timer82C54.skipcycle_last) //Skipped a cycle?
		{
			Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_SKIPCYCLE; //We've skipped a cycle, turn option off!
			setOutput(channel,1); //Set the output to high!
			Timer82C54.channelstatus[channel] |= CHANNELSTATUS_DISABLED; //Disable the channel!
		}
	}
	else //Invalid mode for this?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_SKIPCYCLE; //Turn option off!
		status_82C54_handler[Timer82C54.channelstatus[channel]](channel); //Disabled skip cycle: run like a normal cycle!	
	}
}
Last edited on
Part 2/2
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
void status_running(byte channel) //Running?
{
	byte tocount; //We're to count?
	tocount = 0; //Default: don't count!

	if (Timer82C54.signalrising==0 && Timer82C54.channelcount[channel]!=0) //Input count and got some left?
	{
		if ((gatestatus(channel)==1) && ((getOperatingMode(channel)==3) || (getOperatingMode(channel)==4) || (getOperatingMode(channel)==5))) //Gate high on mode 3/4/5?
		{
			tocount = 1; //Count for input signal!
		}
		else if (getOperatingMode(channel)!=3) //Not mode 3?
		{
			tocount = 1; //Count for input signal!
		}
	}

	if (gatestatus(channel)==0 && (getOperatingMode(channel)==4)) //Counting stops?
	{
		tocount = 0; //Disable count!
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_WAITFORRELOADSET; //Wait for the reload to be set!
		//Output not affected!
		return; //Disable all!
	}
	
	if ((Timer82C54.channelstatus[channel]&CHANNELSTATUS_DISABLED) && (tocount==0)) //Disabled=To continue counting?
	{
		tocount = 1; //Force count!
	}

	if (tocount) //We're at a count clock?
	{
		if (Timer82C54.operatingmode[channel]&1) //BCD mode?
		{
			Timer82C54.channelcount[channel] = encodeBCD((decodeBCD(Timer82C54.channelcount[channel])-1)&0xFFFF); //Decrease counter!
			if (getOperatingMode(channel)==3) //Square wave =one extra!
			{
				Timer82C54.channelcount[channel] = encodeBCD((decodeBCD(Timer82C54.channelcount[channel])-1)&0xFFFF); //Decrease counter!
			}
		}
		else //Binary mode?
		{
			--Timer82C54.channelcount[channel]; //Plain binary mode!
			if (getOperatingMode(channel)==3) //Square wave =one extra!
			{
				--Timer82C54.channelcount[channel]; //Plain binary mode!
			}
		}
		if (Timer82C54.channelcount[channel]==1 && getOperatingMode(channel)==2) //Rate generator 2->1?
		{
			setOutput(channel,0); //Channel goes low!
		}
	}

	if (gatestatus(channel)==1 && ((Timer82C54.inputgate_last&(1<<channel))>>channel)!=1) //Gate rising?
	{
		if (getOperatingMode(channel)==1 || getOperatingMode(channel)==5) //Gate rising causes reload for mode 1&5!
		{
			Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Reload!
		}
		//Now save last status for checking for rise or drop!
		Timer82C54.inputgate_last &= ~(1<<channel); //Disable bit for last status (to set in the next)!
		Timer82C54.inputgate_last |= (gatestatus(channel)<<channel); //Set or reset last!
	}
	else if (gatestatus(channel)==0 && ((Timer82C54.inputgate_last&(1<<channel))>>channel)!=0) //Gate dropping?
	{
		if (getOperatingMode(channel)==2 || getOperatingMode(channel)==6 || getOperatingMode(channel)==3 || getOperatingMode(channel)==7) //Operating mode 2/6 or 3/7?
		{
			Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORGATEHIGH; //Wait for the gate to go high!
			setOutput(channel,1); //Output goes high!
		}
		//Now save last status for checking for rise or drop!
		Timer82C54.inputgate_last &= ~(1<<channel); //Disable bit for last status (to set in the next)!
		Timer82C54.inputgate_last |= (gatestatus(channel)<<channel); //Set or reset last!
	}

	if (Timer82C54.channelstatus[channel]&CHANNELSTATUS_DISABLED) //Disabled?
	{
		if (Timer82C54.channelstatus[channel]&CHANNELSTATUS_DISABLED_RELOADED) //Reloaded?
		{
			Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_DISABLED; //Enable again!
			Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_DISABLED_RELOADED; //Not reloaded anymore!
			Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Reload!
		}
	}

	if ((Timer82C54.channelcount==0) && tocount) //We've reached 0 (we're doing a specific action)?
	{
		Timer82C54.channelstatus[channel] &= ~CHANNELSTATUS_RUNNING; //Done running always when done counting by default!
		switch (getOperatingMode(channel)) //What operating mode?
		{
			case 0: //Interrupt on Terminal count: FULLY IMPLEMENTED!
				setOutput(channel,1); //Pull output high!
				Timer82C54.channelstatus[channel] |= CHANNELSTATUS_WAITFORRELOADSET; //We're going to wait for the reload to be set!
				break;
			case 1: //Hardware Re-triggerable One-shot: FULLY IMPLEMENTED!
				setOutput(channel,1); //Output goes high!
				break;
			case 2: //Mode 2/6 Rate Generator: FULLY IMPLEMENTED! 50%
			case 6:
				setOutput(channel,1); //Go high (again)
				Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Reload!
				break;
			case 3: //Mode 3/7: Square Wave generator 75%
			case 7:
				setOutput(channel,!getOutput(channel)); //Flipflop!
				Timer82C54.channelcount[channel] = Timer82C54.channelreloadvalue[channel]; //Reload!
				break;
			case 4: //Mode 4: Done, except reload! 87.5%
				Timer82C54.skipcycle_last = Timer82C54.signalrising; //Save last cycle skipped!
				if (!Timer82C54.channelstatus[channel]&CHANNELSTATUS_DISABLED) //Not disabled?
				{
					setOutput(channel,0); //Output goes low for one cycle!
					Timer82C54.channelstatus[channel] |= CHANNELSTATUS_SKIPCYCLE; //We're going to wait for the reload to be set!
				}
				break;
			case 5: //Mode 5: 100%
				Timer82C54.skipcycle_last = Timer82C54.signalrising; //Save last cycle skipped!
				setOutput(channel,0); //Output goes low for one cycle!
				Timer82C54.channelstatus[channel] |= CHANNELSTATUS_SKIPCYCLE; //We're going to wait for the reload to be set!
				break;
		}					
	}
}

void status_unknown(byte channel) //Unknown status!
{} //Do nothing!



/*

END OF STATUS HANDLERS (timer)

*/














inline void initStatusHandlers() //Initialize the status handler pointers!
{
	status_82C54_handler[CHANNELSTATUS_INIT] = &status_init; //Init handler!
	word i; //For all possible statuses!
	for (i=0;i<0xFFFF;i++) //Process all possible statuses!
	{
		if (i&CHANNELSTATUS_WAITFORRELOADSETORCOMMANDSET) //Wait for reload/command set?
		{
			status_82C54_handler[i] = &status_waitforreloadsetorcommandset;
		}
		else if (i&CHANNELSTATUS_WAITFORRELOADSET) //Wait for load/reset?
		{
			status_82C54_handler[i] = &status_waitforreloadset;
		}
		else if (i&CHANNELSTATUS_WAITFORGATEHIGH) //Waiting for gate high?
		{
			status_82C54_handler[i] = &status_waitforgatehigh;
		}
		else if (i&CHANNELSTATUS_WAITFORGATELOW) //Waiting for gate low?
		{
			status_82C54_handler[i] = &status_waitforgatelow;
		}
		else if (i&CHANNELSTATUS_SKIPCYCLE) //Skip a cycle?
		{
			status_82C54_handler[i] = &status_skipcycle;
		}
		else if (i&CHANNELSTATUS_RUNNING) //Running channel is final priority?
		{
			status_82C54_handler[i] = &status_running;
		}
		else //Unknown status?
		{
			status_82C54_handler[i] = &status_unknown;
		}
	}
}







inline void handleTimer(byte channel) //Handle a timer tick for a channel!
{
	//byte channelstatus; //Channel status?
	//channelstatus = Timer82C54.channelstatus[channel]; //Active channel status!

	if ((Timer82C54.channelstatus[channel]&CHANNELSTATUS_WAITFORHIBYTE) && modeblockhi(getOperatingMode(channel))) //Waiting for hi-byte and this blocks operations?
	{
		//Waiting for high byte: no channel does something on this!
		return; //Waiting for high byte!
	}

	status_82C54_handler[Timer82C54.channelstatus[channel]](channel); //Handle the status!
}

extern int EMU_RUNNING; //Emulator running?
void timerfrequencyhandler() //Handles a high or drop for frequency!
{
	if (!EMU_RUNNING) return; //Disable this: we're not running the CPU, so useless!
	Timer82C54.signalrising = !Timer82C54.signalrising; //Rise or drop!
	handleTimer(0); //Handle timer #0!
	handleTimer(1); //Handle timer #1!
	handleTimer(2); //Handle timer #2!
}
Last edited on
Topic archived. No new replies allowed.