Created
September 7, 2021 23:30
-
-
Save erique/12fa84f568398d70bb76be6eeabe4503 to your computer and use it in GitHub Desktop.
Simple Amiga timer.device example (50Hz continuous trigger)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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