Skip to content

Instantly share code, notes, and snippets.

@erique
Created September 7, 2021 23:30
Show Gist options
  • Save erique/12fa84f568398d70bb76be6eeabe4503 to your computer and use it in GitHub Desktop.
Save erique/12fa84f568398d70bb76be6eeabe4503 to your computer and use it in GitHub Desktop.
Simple Amiga timer.device example (50Hz continuous trigger)
// m68k-amigaos-gcc -Wall -Wno-pointer-sign -noixemul timer_example.c -o timer_example
#include <stdio.h>
#include <exec/types.h>
#include <exec/interrupts.h>
#include <exec/io.h>
#include <dos/dos.h>
#include <inline/exec.h>
#include <inline/timer.h>
#include <devices/timer.h>
#include <hardware/custom.h>
const uint32_t deltaTimeUS = 20000; // 20k microseconds == 50Hz
struct TimerState
{
struct Interrupt isr;
struct MsgPort msgPort;
struct timerequest* timerReq;
uint64_t eticksStartup;
uint32_t iterations;
volatile uint8_t quit;
};
// This is the SoftInt code
void is_Code(void* is_Data __asm("a1"))
{
struct TimerState* state = (struct TimerState*) is_Data;
// Remove the message from the queue
struct ExecBase* SysBase = (*((struct ExecBase**) 4));
GetMsg(&state->msgPort);
// If we're done, we're done.
if (state->quit)
return;
// Set the background color as some kind of visual feedback
struct Custom* custom = (struct Custom*)0xdff000;
custom->color[0] = 0x0f0f;
// Get the current ECLK tick and freq (709/714 kHz)
struct timerequest* timerReq = state->timerReq;
struct Device* TimerBase = timerReq->tr_node.io_Device;
uint64_t eticks;
uint32_t efreq = ReadEClock((struct EClockVal*)&eticks);
eticks -= state->eticksStartup;
// Compare perceived wall clock (sum of all delta intervals) to actual wall clock (ECLK)
// Compensate the next delay with the difference..
uint64_t microsElapsed = (1000 * 1000 * eticks / efreq);
uint64_t microsNominal = state->iterations * deltaTimeUS;
uint32_t compensationUS = (uint32_t)(microsElapsed - microsNominal) / 2;
if (compensationUS > deltaTimeUS)
compensationUS = 0;
state->iterations++;
// Kick off another timer request; i.e. "sleep" for N microseconds
timerReq->tr_node.io_Command = TR_ADDREQUEST;
timerReq->tr_time.tv_micro = deltaTimeUS - compensationUS;
SendIO((struct IORequest*)timerReq);
}
int main(const int argc, const char** argv)
{
struct ExecBase* SysBase = (*((struct ExecBase**) 4));
printf("%s - press CTRL-C to quit..\n", argv[0]);
fflush(stdout);
struct TimerState state = { 0 };
// Set up a message port that would signal our ISR
struct Interrupt* isr = &state.isr;
struct MsgPort* msgPort = &state.msgPort;
msgPort->mp_Node.ln_Type = NT_MSGPORT;
msgPort->mp_Flags = PA_SOFTINT;
msgPort->mp_SigTask = isr;
// Set up the ISR itself
isr->is_Node.ln_Pri = 32;
isr->is_Data = &state;
isr->is_Code = is_Code;
// Create the timer request and open the timer device
state.timerReq = CreateIORequest(msgPort, sizeof(struct timerequest));
struct IORequest* ioReq = (struct IORequest*)state.timerReq;
OpenDevice(TIMERNAME, UNIT_MICROHZ, ioReq, 0L);
// Measure wall clock time
struct Device* TimerBase = ioReq->io_Device;
struct timeval beforeTime;
GetSysTime(&beforeTime);
// Sample our "epoch" and bootstrap the ISR
ReadEClock((struct EClockVal*)&state.eticksStartup);
Cause(isr);
// Wait for CTRL-C
while (1)
{
uint32_t sigs = Wait(SIGBREAKF_CTRL_C);
if (sigs & SIGBREAKF_CTRL_C)
break;
}
// Signal that time is up, and abort current request
state.quit = 1;
while (!(CheckIO(ioReq)))
AbortIO(ioReq);
// Meassure wall clock time again, and calc difference
struct timeval afterTime;
GetSysTime(&afterTime);
SubTime(&afterTime, &beforeTime);
printf("Num iterations = %d\n", state.iterations);
uint32_t totalMillisElapsed = afterTime.tv_secs * 1000 + afterTime.tv_micro / 1000;
uint32_t totalCallbackTime = deltaTimeUS * state.iterations / 1000;
printf("Spent %d ms for %d ms worth of callbacks\n", totalMillisElapsed, totalCallbackTime );
CloseDevice(ioReq);
DeleteIORequest(ioReq);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment