Compare commits

..

3 Commits

Author SHA1 Message Date
1e1a28e23d starts work on the PIT 2025-06-06 12:47:02 -04:00
3e01dc2074 massive overhaul to my exception system 2025-06-05 20:28:17 -04:00
79f04df82e my exception handler sucked so i redid it 2025-06-05 17:32:38 -04:00
7 changed files with 520 additions and 37 deletions

View File

@ -3,7 +3,7 @@
CC = i686-elf-gcc CC = i686-elf-gcc
DEBUG_FLAGS = -D__TESTING__ -g DEBUG_FLAGS = -D__TESTING__ -g
CFLAGS = -m32 -ffreestanding -Wall -Wextra -Werror -Wpedantic --sysroot=$(PWD)/sysroot -isystem=/usr/include -Iinclude -MD $(DEBUG_FLAGS) CFLAGS = -m32 -ffreestanding -Wall -Wextra -Werror -Wpedantic --sysroot=$(PWD)/sysroot -isystem=/usr/include -Iinclude -MD $(DEBUG_FLAGS)
@ -39,7 +39,7 @@ all: kernel.elf
env: install-headers install-libs # TODO: change this to a file specific target, so it doesnt recompile everytime, and so we can move as a prereq on kernel.elf env: install-headers install-libs # TODO: change this to a file specific target, so it doesnt recompile everytime, and so we can move as a prereq on kernel.elf
util/./gen-clangd.sh util/./gen-clangd.sh
kernel.elf: install-headers install-libs $(c_kern_objects) $(asm_kern_objects) kernel.elf: install-headers install-libs env $(c_kern_objects) $(asm_kern_objects)
$(CC) $(CFLAGS) -T $(shell find . -name "link.ld") -o $@ $(c_kern_objects) $(asm_kern_objects) -nostdlib -lk -lgcc $(CC) $(CFLAGS) -T $(shell find . -name "link.ld") -o $@ $(c_kern_objects) $(asm_kern_objects) -nostdlib -lk -lgcc
os.iso: kernel.elf os.iso: kernel.elf

34
TODO.md
View File

@ -1,7 +1,33 @@
## What do I need to do? ## What do I need to do?
Exception handling is a must for next part, we need to be able to dianogse issues better
- Want the address of the fault
- Proper handling of the issue
- Maybe dump registers? PS/2 controller
-> need to setup some timing device, probably PIT
-> need to setup the USB controller first, so i can disable Legacy USB bios shit
-> need to configure ACPI so i can actually detect for the PS/2 controller
-> then we can start configuring the PS/2
-> to properly send data to PS/2 controllers, we need some sort of timing interface,
this way we can timeout if we're being hanged on sending commands to a ps/2 device
PIT timer, or some sort of timer interface
perhaps maybe some more things off of the PIC? im not sure
really after that I want to do more to work towards userspace, so that I can start creating things
scheduler?
multi processing? threads?
etc
### Features I want
- A kernel bus
- some kind of driver where I can send messages that can eventually be accessed globally by other drives / applications

View File

@ -1,9 +1,9 @@
#include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#ifdef __TESTING__
#include <kernel/_kernel.h>
#include <stdio.h> #include <stdio.h>
#endif
#include <kernel/_kernel.h>
#include <kernel/x86/idt.h> #include <kernel/x86/idt.h>
#include <kernel/x86/pic.h> #include <kernel/x86/pic.h>
#include <kernel/x86/io.h> #include <kernel/x86/io.h>
@ -18,20 +18,218 @@ static bool vectors[IDT_MAX_DESCRIPTORS];
extern struct keyboard_state keyb_state; extern struct keyboard_state keyb_state;
void exception_handler(unsigned int i)
#define EXTERNAL_BIT (0x1)
#define TBL_GDT (0x0)
#define TBL_LDT (0x2)
#define TBL_IDT (0x1)
#define TBL_IDT_TWO (0x3)
static void examine_selector(uint32_t selector)
{ {
if (i <= 31) { // TODO: implement proper handling for each exception, also implement the proper gates & error code checking printf("The interrupt arised %s.\n", (selector & EXTERNAL_BIT) == EXTERNAL_BIT ? "externally" : "internally");
#ifdef __TESTING__
kerror("EXCEPTION");
printf("Exeption: %u\n", i); printf("The descriptor 0x%x, is located in the ", selector, (selector & 0x0000FFFF) >> 3);
#endif if ((((selector >> 1) & TBL_IDT) == TBL_IDT)
__asm__ volatile ("cli; hlt"); // hangs the computer || ((selector >> 1) & TBL_IDT_TWO) == TBL_IDT_TWO) {
puts("IDT.");
} else if (((selector >> 1) & TBL_LDT) == TBL_LDT) {
puts("LDT.");
} else {
puts("GDT.");
}
return;
}
#undef TBL_GDT
#undef TBL_LDT
#undef TBL_IDT
#undef TBL_IDT_TWO
#define PF_P (1 << 0)
#define PF_W (1 << 1)
#define PF_U (1 << 2)
#define PF_R (1 << 3)
#define PF_I (1 << 4)
#define PF_PK (1 << 5)
#define PF_SS (1 << 6)
#define PF_SGX (1 << 15)
static void examine_page_fault(uint32_t error_code)
{
puts("Page fault:");
if (error_code & PF_P) {
puts("Page-protection violation.");
} else {
puts("Not present page.");
} }
if (i == PIC_KEYB) { if (error_code & PF_W) {
unsigned char in = inb(0x60); puts("Write access.");
do_keypress(decode_scancode(in)); } else {
PIC_sendEOI(1); puts("Read access.");
}
if (error_code & PF_U) {
puts("In CPL 3.");
}
if (error_code & PF_R) { // Only applies if PSE or PAE are set in CR4
puts("One or more page directory entires contain reserved bits which are set to 1.");
}
if (error_code & PF_I) { // Only applicable when the No-Execute bit is supported and enabled
puts("Instruction fetch.");
}
if (error_code & PF_PK) {
puts("Protection-key violation"); //TODO PRKU (user mode access) or PRKS MSR (supervisor mode access) specifies the key rights
}
if (error_code & PF_SS) {
puts("Shadow stack access.");
}
if (error_code & PF_SGX) { // Unrelated to ordinary paging
puts("SGX vioilation");
}
// TODO, also sets CR2 reg to the virtual address which caused the page fault
}
#undef PF_P
#undef PF_W
#undef PF_U
#undef PF_R
#undef PF_I
#undef PF_PK
#undef PF_SS
#undef PF_SGX
#define EXCEPTION_LOCATION() printf("Exception occurred at 0x%2\n", stack.eip)
void exception_handler(struct cpu_state __attribute__((unused)) cpu, uint32_t interrupt, struct stack_state stack)
{
uint32_t inbyte;
switch (interrupt) {
/** EXCEPTIONS BEGIN **/
case EXCEPT_DIV_ERR:
kerror("EXCEPTION: DIV BY ZERO");
EXCEPTION_LOCATION();
break;
case EXCEPT_DEBUG:
kinfo("EXCEPTION: DEBUG"); // TODO: this one has more specific requirements
break;
case EXCEPT_NMI:
kinfo("EXCEPTION: NON-MASKABLE INTERRUPT"); // TODO
break;
case EXCEPT_BREAKPOINT:
kinfo("EXCEPTION: BREAKPOINT");
// TODO
break;
case EXCEPT_OVERFLOW:
kerror("EXCEPTION: OVERFLOW");
break;
case EXCEPT_BOUND_RANGE_EXCEEDED:
kerror("EXCEPTION: BOUND RANGE EXCEEDED");
EXCEPTION_LOCATION();
break;
case EXCEPT_INVALID_OPCODE:
kerror("EXCEPTION: INVALID OPCODE");
EXCEPTION_LOCATION();
break;
case EXCEPT_DEVICE_NOT_AVAILABLE:
kerror("EXCEPTION: DEVICE NOT AVAILABLE");
printf("Instructions expecting an FPU were used, but no FPU was found. Is it disabled in CR0? Do you have an FPU?\n");
EXCEPTION_LOCATION();
break;
case EXCEPT_DOUBLE_FAULT:
kerror("EXCEPTION: DOUBLE FAULT");
__asm__ volatile ("cli; hlt"); // boned
break;
case EXCEPT_INVALID_TSS:
kerror("EXCEPTION: INVALID TSS");
examine_selector(stack.error_code);
// TODO: when i implement the TSS, the EIP will be one of two things
// if this occurs before loading segmenet selectors from the TSS, EIP will be the instruction that caused the instruction
// otherwise its the first instruction in the new task
break;
case EXCEPT_SEG_NOT_PRESENT:
kerror("EXCEPTION: SEGMENT NOT PRESENT");
EXCEPTION_LOCATION();
examine_selector(stack.error_code);
break;
case EXCEPT_STACK_SEG_FAULT:
kerror("EXCEPTION: STACK-SEGMENT FAULT");
if (stack.error_code == 0) {
EXCEPTION_LOCATION();
} else {
printf("Tried loading non-present stack segment during hardware task switch.\n");
examine_selector(stack.error_code);
}
break;
case EXCEPT_GENERAL_PROTECTION:
kerror("EXCEPTION: GENERAL PROTECTION FAULT");
EXCEPTION_LOCATION();
if (stack.error_code != 0)
examine_selector(stack.error_code);
break;
case EXCEPT_PAGE_FAULT:
kerror("EXCEPTION: PAGE FAULT");
EXCEPTION_LOCATION();
examine_page_fault(stack.error_code);
break;
case EXCEPT_FLOATING_POINT_ERR_FPU:
kerror("EXCEPTION: FLOATING POINT ERROR FPU");
// TODO: this requires a custom handler, it needs another register
break;
case EXCEPT_ALIGNMENT_CHECK:
kerror("EXCEPTION: ALIGNMENT CHECK");
// TODO: only in CPL3
break;
case EXCEPT_MACHINE_CHECK:
kerror("EXCEPTION: MACHINE CHECK");
// TODO disabled by default
break;
case EXCEPT_FLOATING_POINT_ERR_SIMD:
kerror("EXCEPTION: FLOATING POINT ERROR SIMD");
// TODO disabled by default
break;
case EXCEPT_VIRT:
kerror("EXCEPTION: VIRTUALIZATION");
break;
case EXCEPT_CTRL_PROT:
kerror("EXCEPTION: CONTROL PROTECTION");
printf("Error code: 0x%2\n", stack.error_code);
break;
case EXCEPT_HYPERVISOR_INJECTION:
kerror("EXCEPTION: HYPERVISOR INJECTION");
break;
case EXCEPT_VMM_COMMUNICATION:
kerror("EXCEPTION: VMM COMMUNICATION");
printf("Error code: 0x%2\n", stack.error_code);
break;
case EXCEPT_SECURITY_EXCEPTION:
kerror("EXCEPTION: SECURITY");
printf("Error code: 0x%2\n", stack.error_code);
break;
/** EXCEPTIONS END **/
/** PIC BEGIN **/
case PIC_KEYB:
inbyte = inb(0x60);
do_keypress(decode_scancode(inbyte));
PIC_sendEOI(1);
break;
/** PIC END **/
default:
kerror("EXCEPTION: UNHANDLED EXCEPTION OR INTERRUPT");
printf("Error code: 0x%2\n", stack.error_code);
break;
} }
} }
@ -66,7 +264,7 @@ void idt_init(void)
PIC_remap(0x20, 0x28); PIC_remap(0x20, 0x28);
pic_disable(); // mask everything pic_disable(); // mask everything
IRQ_clear_mask(1); IRQ_clear_mask(1);
__asm__ volatile("sti"); // set the interrupt flag __asm__ volatile("sti"); // set the interrupt flag
#ifdef __TESTING__ #ifdef __TESTING__
kinfo("Initialized the IDT"); kinfo("Initialized the IDT");

View File

@ -1,27 +1,47 @@
extern exception_handler extern exception_handler
extern exception_handler_err
%macro isr_err_stub 1 %macro isr_err_stub 1
isr_stub_%+%1: isr_stub_%+%1:
pushad push dword %1 ; push the interrupt number
cld jmp common_interrupt_handler
push dword %1
call exception_handler
pop eax ; make sure to pop off the dword!!
popad
iret
%endmacro %endmacro
%macro isr_no_err_stub 1 %macro isr_no_err_stub 1
isr_stub_%+%1: isr_stub_%+%1:
pushad push dword 0
cld
push dword %1 push dword %1
call exception_handler jmp common_interrupt_handler
pop eax
popad
iret
%endmacro %endmacro
common_interrupt_handler:
; lets save the registers
push eax
push ebx
push ecx
push edx
push esi
push edi
push ebp
call exception_handler
; restore the registers
pop ebp
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
; lets move the stack before the error code and interrupt number
; since we don't want these to be popped anywhere
add esp, 8
iret
isr_no_err_stub 0 isr_no_err_stub 0
isr_no_err_stub 1 isr_no_err_stub 1
isr_no_err_stub 2 isr_no_err_stub 2

3
kernel/arch/io/pit.c Normal file
View File

@ -0,0 +1,3 @@
#include <kernel/x86/pit.h>

View File

@ -15,7 +15,6 @@
#define EXCEPT_INVALID_OPCODE 6 #define EXCEPT_INVALID_OPCODE 6
#define EXCEPT_DEVICE_NOT_AVAILABLE 7 #define EXCEPT_DEVICE_NOT_AVAILABLE 7
#define EXCEPT_DOUBLE_FAULT 8 #define EXCEPT_DOUBLE_FAULT 8
#define EXCEPT_SEG_OVERRUN 9
#define EXCEPT_INVALID_TSS 10 #define EXCEPT_INVALID_TSS 10
#define EXCEPT_SEG_NOT_PRESENT 11 #define EXCEPT_SEG_NOT_PRESENT 11
#define EXCEPT_STACK_SEG_FAULT 12 #define EXCEPT_STACK_SEG_FAULT 12
@ -27,8 +26,28 @@
#define EXCEPT_FLOATING_POINT_ERR_SIMD 19 #define EXCEPT_FLOATING_POINT_ERR_SIMD 19
#define EXCEPT_VIRT 20 #define EXCEPT_VIRT 20
#define EXCEPT_CTRL_PROT 21 #define EXCEPT_CTRL_PROT 21
#define EXCEPT_HYPERVISOR_INJECTION 28
#define EXCEPT_VMM_COMMUNICATION 29
#define EXCEPT_SECURITY_EXCEPTION 30
void exception_handler(unsigned int i); struct cpu_state {
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
} __attribute__((packed));
struct stack_state {
uint32_t error_code;
uint32_t eip;
uint32_t cs;
uint32_t eflags;
} __attribute__((packed));
void exception_handler(struct cpu_state cpu, uint32_t interrupt, struct stack_state stack);
typedef struct { typedef struct {
uint16_t isr_low; // The lower 16 bits of the ISR's address uint16_t isr_low; // The lower 16 bits of the ISR's address

View File

@ -0,0 +1,217 @@
#ifndef ARCH_PIT_H
#define ARCH_PIT_H
#define PIT_CHANNEL_0 (0x40) // R/W
#define PIT_CHANNEL_1 (0x41) // R/W
#define PIT_CHANNEL_2 (0x42) // R/W
#define PIT_CMD_REG (0x43) // W
/**
* Selects which channel is being configured.
* Must always be valid on every write to the CMD register
*
* READ_BACK isn't always supported
*/
enum PIT_CHANNEL {
CHANNEL_0 = 0x0,
CHANNEL_1,
CHANNEL_2,
READ_BACK // 8254 only
};
/**
* Tells the PIT which access mode to use for the selected channel. (Or counter latch cmd)
* These must be valid on every write to the CMD register.
*
* LOBYTE indicates that only the low 8 bytes of a (16-bit) value will be transmitted to the lower bit of the register
* HIBYTE is the same, but for the higher value
* HI AND LOW means that the values will come sequentially through the IO port, lowest followed by highest
*/
enum PIT_ACCESS_MODE {
LATCH_COUNT_VAL_CMD = 0x0,
LO_BYTE_ONLY,
HI_BYTE_ONLY,
HI_AND_LO_BYTE
};
/**
* While each operating mode functions differently, they all have these in common:
*
* Everytime the cmd register is written to, all interal logic on the selected PIT channel is reset, the output will immediately revert to the mode's initial state
*
* A new reload value can be written to a PIT channel's data port at any time.
*
* The current counter value is always either decremented or reset to the reload value on the *falling* edge of the input signal.
*
* In modes where the current count is decremented when it is reloaded, the current count is not decremented on the same input clock pulse as the reload
* it starts decrementing on the next input clock pulse
*
*/
enum PIT_OPERATING_MODE {
/**
* **MODE 0**
* When the cmd reigster is written, the output signal goes low and the PIT waits for the reload register to be set by software, to begin the countdown.
* After the reload register has been set, the current count will be set to the reload value on the next falling edge of the signal.
* Subsequent falling edges of the input signal will decrement the current count, (if the gate input is high on the preceding rising edge of the input signal).
*
* When the current count decrements from one to zero, the output goes high and remains high until another cmd register is written, or the reload register is set again.
* The current count will wrap around to 0xFFFF (0x9999 in BCD) and continue to decrement until the cmd register or the reload register are set, however this will not affect the output pin state.
*
* The reload value can be changed at any time. In "lobyte/hibyte" access mode counting will stop when the first byte of the reload value is set.
* Once the full reload value is set (in any access mode), the next falling edge of the input signal will cause the new reload value to be copied into the current count,
* and the countdown will continue from the new value.
*
* This mode only generates interrupts on channel zero.
*
*/
MODE_0 = 0x0, // Interrupt on terminal count
/**
* **MODE 1**
* This mode is similar to mode 0, however counting doesn't start until a rising edge of the gate input is detected.
* For this reason it is not usable for PIT channels 0 or 1 (where the gate input can't be changed).
*
* When the CMD register is written, the output signal goes high and the PIT waits for the reload register to be set by software.
* After the reload register has been set, the PIT will wait for the next rising edge of the gate input.
* Once this occurs, the output signal will go low and the current count will be set to the reload value on the next falling edge of the input signal.
* Subsequent falling edges of the input signal will decrement the current count.
*
* When the current count decrements from one to zero, the output goes high and remains high until another cmd register is written or the reload register is set again.
* The current count will wrap around to 0xFFFF (0x9999 in BCD) and continue to decrement until the cmd register or the reload register are set, however this will not affect the output pin state.
*
* If the gate input signal goes low during this process it will have no effect.
* However, if the gate input goes high again it will cause the current count to be reloaded from the reload register on the next falling edge of the input signal,
* and restart the count again (the same as when counting first started).
*
* The reload value can be changed at any time, however the new value will not affect the current count until the current count is reloaded (on the next rising edge of gate input)
* So if you want to do this, clear and then reset bit 0 of IO port 0x61, after modifying the reload value.
*/
MODE_1, // hardware re-triggerable one-shot
/**
* **MODE 2**
* This mode operates as a frequency divider.
*
* When the mode/command register is written the output signal goes high and the PIT waits for the reload register to be set by software. After the reload register has been set, the current count will be set to the reload value on the next falling edge of the (1.193182 MHz) input signal. Subsequent falling edges of the input signal will decrement the current count (if the gate input is high on the preceding rising edge of the input signal).
*
* When the current count decrements from two to one, the output goes low, and on the next falling edge of the (1.193182 MHz) input signal it will go high again and the current count will be set to the reload value and counting will continue.
*
* If the gate input goes low, counting stops and the output goes high immediately. Once the gate input has returned high, the next falling edge on input signal will cause the current count to be set to the reload value and operation will continue.
*
* The reload value can be changed at any time, however the new value will not affect the current count until the current count is reloaded (when it is decreased from two to one, or the gate input going low then high). When this occurs counting will continue using the new reload value.
*
* A reload value (or divisor) of one must not be used with this mode.
*
* This mode creates a high output signal that drops low for one input signal cycle (0.8381 uS), which is too fast to make a difference to the PC speaker (see mode 3). For this reason mode 2 is useless for producing sounds with PIT channel 2.
*
* Typically, OSes and BIOSes use mode 3 (see below) for PIT channel 0 to generate IRQ 0 timer ticks, but some use mode 2 instead, to gain frequency accuracy (frequency = 1193182 / reload_value Hz).
*/
MODE_2, // rate generator
/**
* **MODE 3**
*
* For mode 3, the PIT channel operates as a frequency divider like mode 2,
however the output signal is fed into an internal "flip flop" to produce a square wave (rather than a short pulse).
* The flip flop changes its output state each time its input state (or the output of the PIT channel's frequency divider) changes.
* This causes the actual output to change state half as often, so to compensate for this the current count is decremented twice on each falling edge of the input signal (instead of once),
* and the current count is set to the reload value twice as often.
*
* When the mode/command register is written the output signal goes high and the PIT waits for the reload register to be set by software.
* After the reload register has been set, the current count will be set to the reload value on the next falling edge of the (1.193182 MHz) input signal.
* Subsequent falling edges of the input signal will decrement the current count twice (if the gate input is high on the preceding rising edge of the input signal).
*
* Note: under normal circumstances the output state will be low 50% of the time when the mode/command register is written.
* The output will then go high, which will generate an immediate (perhaps spurious) IRQ0. The other 50% of the time the output will already be high, and there will be no IRQ0 generated.
*
* For even reload values, when the current count decrements from two to zero the output of the flip-flop changes state;
* the current count will be reset to the reload value and counting will continue.
*
* For odd reload values, the current count is always set to one less than the reload value.
* If the output of the flip flop is low when the current count decrements from two to zero it will behave the same as the equivalent even reload value.
* However, if the output of the flip flop is high the reload will be delayed for one input signal cycle (0.8381 uS),
* which causes the "high" pulse to be slightly longer and the duty cycle will not be exactly 50%.
* Because the reload value is rounded down to the nearest even number anyway,
* it is recommended that only even reload values be used (which means you should mask the value before sending it to the port).
*
* Note: This even value limitation on the reload value in mode 3 reduces the number of possible output frequencies in half.
* If you want to be able to control the frequency of IRQ0 to a somewhat higher degree, then think about using mode 2 instead for channel 0.
*
* On channel 2, if the gate input goes low, counting stops and the output goes high immediately.
* Once the gate input has returned high, the next falling edge on input signal will cause the current count to be set to the reload value and operation will continue (with the output left high).
*
* The reload value can be changed at any time, however the new value will not affect the current count until the current count is reloaded
* (when it is decreased from two to zero, or the gate input going low then high). When this occurs counting will continue using the new reload value.
*
* A reload value (or divisor) of one must not be used with this mode.
*/
MODE_3, // square wave generator
/**
* **MODE 4**
*
* Mode four operates as a retriggerable delay, and generates a pulse when the current count reaches zero.
*
* When the mode/command register is written the output signal goes high and the PIT waits for the reload register to be set by software.
* After the reload register has been set, the current count will be set to the reload value on the next falling edge of the (1.193182 MHz) input signal.
* Subsequent falling edges of the input signal will decrement the current count (if the gate input is high on the preceding rising edge of the input signal).
*
* When the current count decrements from one to zero, the output goes low for one cycle of the input signal (0.8381 uS).
* The current count will wrap around to 0xFFFF (or 0x9999 in BCD mode) and continue to decrement until the mode/command register or the reload register are set,
* however this will not affect the output state.
*
* If the gate input goes low, counting stops but the output will not be affected and the current count will not be reset to the reload value.
*
* The reload value can be changed at any time.
* When the new value has been set (both bytes for "lobyte/hibyte" access mode) it will be loaded into the current count on the next falling edge of the (1.193182 MHz) input signal,
* and counting will continue using the new reload value.
*/
MODE_4, // software triggered strobe
/**
* **MODE 5**
* Mode 5 is similar to mode 4, except that it waits for the rising edge of the gate input to trigger (or re-trigger) the delay period (like mode 1).
* For this reason it is not usable for PIT channels 0 or 1 (where the gate input can't be changed).
*
* When the mode/command register is written the output signal goes high and the PIT waits for the reload register to be set by software.
* After the reload register has been set the PIT will wait for the next rising edge of the gate input.
* Once this occurs, the current count will be set to the reload value on the next falling edge of the (1.193182 MHz) input signal.
* Subsequent falling edges of the input signal will decrement the current count.
*
* When the current count decrements from one to zero, the output goes low for one cycle of the input signal (0.8381 uS).
* The current count will wrap around to 0xFFFF (or 0x9999 in BCD mode) and continue to decrement until the mode/command register or the reload register are set,
* however this will not affect the output state.
*
* If the gate input signal goes low during this process it will have no effect.
* However, if the gate input goes high again it will cause the current count to be reloaded from the reload register on the next falling edge of the input signal,
* and restart the count again (the same as when counting first started).
*
* The reload value can be changed at any time, however the new value will not affect the current count until the current count is reloaded (on the next rising edge of the gate input).
* When this occurs counting will continue using the new reload value.
*/
MODE_5, // hardware triggered strobe
_MODE_2, // mode 2
_MODE_3 // mode 3
};
/**
* Binary mode is the standard value,
* where as BCD will hold a single decimal value in each of the four bits
* The counter for BCD goes to 0x9999, instead of 0xFFFF
*
* BCD is not properly implemented on all chips, even if they say they support it.
*/
enum PIT_DIGIT_MODE {
BINARY_MODE = 0x0, // 16-bit binary
BCD_MODE // four-digit bcd
};
#endif