diff --git a/doc/pic.drawio b/doc/pic/pic.drawio similarity index 100% rename from doc/pic.drawio rename to doc/pic/pic.drawio diff --git a/doc/pic/pic.drawio.png b/doc/pic/pic.drawio.png new file mode 100644 index 0000000..30180f2 Binary files /dev/null and b/doc/pic/pic.drawio.png differ diff --git a/doc/pic/pic.md b/doc/pic/pic.md new file mode 100644 index 0000000..e925aad --- /dev/null +++ b/doc/pic/pic.md @@ -0,0 +1,60 @@ +# PIC + +## Overview + +The PIC (Programmable Interrupt Controller) supports 256 edge or level +triggered interrupts. + +Interrupts can only be rising edge or positive level trigger. Negative edge or +low level trigger can be accomplished by inverting the input to this module. + +When an interrupt is triggered, it remains triggered until cleared with an +`EOI`. Currently, only the highest priority interrupt can be cleared. This +is bad design and will be fixed soon(tm). + +![pic.drawio.png](pic.drawio.png) + +## Interface + +The PIC is controlled through two 8-bit registers, `CMD` and `DAT`. +The upepr 3 bits of `CMD` control which register is accessed, and the lower 5 +bits control the byte select. + +`EOI` is a special case: when `CMD` is 0xFF, the highest priority interrupt is +cleared. + +### CMD +| [7:5] | [4:0] | +|------------------ |------------- | +| Register Address | Byte Select | + +### DAT +| [7:0] | +|----------------------- | +| Data[8*Byte_sel +: 8] | + +## Registers + +### Register Map + +| Address | Read | Write | +|--------- |------------ |-------- | +| 0 | IRQ Number | ~ | +| 1 | Enable | Enable | +| 2 | Type | Type | + +### IRQ Number + +8 bit register outputs the highest priority interrupt currently active. When +no interrupt is active, it will output 255. + +### Enable + +256 bit enable register. When bit _n_ is set to 1, the corresponding interrupt +_n_. + +### Type + +256 bit type register. When bit _n_ is set to 0, the corresponding interrupt +_n_ will be edge triggered. If the bit is set to 1, the interrupt will be level +triggered. \ No newline at end of file diff --git a/doc/rtc.drawio b/doc/rtc/rtc.drawio similarity index 100% rename from doc/rtc.drawio rename to doc/rtc/rtc.drawio diff --git a/doc/rtc/rtc.drawio.png b/doc/rtc/rtc.drawio.png new file mode 100644 index 0000000..993ffb2 Binary files /dev/null and b/doc/rtc/rtc.drawio.png differ diff --git a/doc/rtc/rtc.md b/doc/rtc/rtc.md new file mode 100644 index 0000000..5d68bd5 --- /dev/null +++ b/doc/rtc/rtc.md @@ -0,0 +1,82 @@ +# RTC + +## Overview + +The RTC is capable of keeping track of elapsed time as well as generating +periodic interrupts. Its input frequency is (currently) the same as the CPU +frequency. The architecture is shown below: + +![rtc.drawio.png](rtc.drawio.png) + +The `counter` register is a 32 bit register which increments by `increment` +every clock tick. When this value reaches `threshold`, both `output` and +`irq_counter` are incremented by 1. When `irq_counter` reaches `irq_threshold`, +an interrupt is generated. + +The interrupt frequency can be calculated with the following equations: + +$$ F_{out}=\frac{F_{in}\cdot{increment}}{threshold} $$ +$$ F_{irq}=\frac{F_{out}}{irq\_threshold} $$ + +Where $ F_{out} $ is the frequency at which the output changes and $ F_{irq} $ +is the frequency at which interrupts occur. + +## Interface + +The RTC is controlled through two 8-bit registers, `CMD` and `DAT`. +The upepr 4 bits of `CMD` control which register is accessed, and the lower 4 +bits control the byte select. The RTC uses 32 bit registers, so only values of +0-3 are supported. + +### CMD +| [7:4] | [3:2 | [1:0] | +|------------------ |---------- |------------- | +| Register Address | Reserved | Byte Select | + +### DAT +| [7:0] | +|----------------------- | +| Data[8*Byte_sel +: 8] | + +## Registers + +### Register Map + +| Address | Read | Write | +|--------- |--------------- |--------------- | +| 0 | Threshold | Threshold | +| 1 | Increment | Increment | +| 2 | IRQ Threshold | IRQ Threshold | +| 3 | Output | Control | + +### Threshold + +32 bit threshold register. When `counter` reaches this value, `output` and +`irq_counter` are incremented by 1, and `counter` is reset back to 0. + +### Increment + +32 bit increment register. Every clock cycle, `counter` is incremented by +`increment` + +### IRQ Threshold + +32 bit IRQ threshold register. When `irq_counter` reaches this value, and IRQ +is generated and `irq_counter` is reset back to 0. + +### Output + +32 bit output register. This value ticks up. + +### Control + +| [7:2] | [1] | [0] | +|---------- |------------ |-------- | +| Reserved | IRQ Enable | Enable | + +8 bit Control register. Regardless of Byte Select, any write to address 3 will +write to this register. `IRQ Enable` controls interrupt generation. `Enable` is +a global enable. When 0, all counter registers are set to 0. + + + diff --git a/sw/kernel/devices/interrupt_controller.h b/sw/kernel/devices/interrupt_controller.h new file mode 100644 index 0000000..b016676 --- /dev/null +++ b/sw/kernel/devices/interrupt_controller.h @@ -0,0 +1,32 @@ +#ifndef _INTERRUPT_CONTROLLER_H +#define _INTERRUPT_CONTROLLER_H + +#include + +// These need to be copied in interrupt_controller.s + +#define IRQ_CMD_ADDR 0xeffc +#define IRQ_DAT_ADDR 0xeffd + +#define IRQ_CMD_MASK 0xe0 +#define IRQ_REG_MASK 0x1f + +#define IRQ_CMD_READIRQ 0x00 +#define IRQ_CMD_ENABLE 0x20 +#define IRQ_CMD_TYPE 0x40 +#define IRQ_CMD_EOI 0xff + +#define IRQ_EDGE 0 +#define IRQ_LEVEL 1 + + + +void init_interrupt_controller(); + +void enable_irq(uint8_t type, uint8_t irqnum); +void disable_irq(uint8_t irqnum); + +// This should accept irqnum later. +void send_eoi(); + +#endif \ No newline at end of file diff --git a/sw/kernel/devices/interrupt_controller.s b/sw/kernel/devices/interrupt_controller.s new file mode 100644 index 0000000..b7cf594 --- /dev/null +++ b/sw/kernel/devices/interrupt_controller.s @@ -0,0 +1,136 @@ +.MACPACK generic + +.autoimport + +.importzp tmp1, tmp2 + +.export _init_interrupt_controller +.export _enable_irq +.export _disable_irq +.export _send_eoi + +IRQ_CMD_ADDR = $effc +IRQ_DAT_ADDR = $effd + +IRQ_CMD_MASK = $e0 +IRQ_REG_MASK = $1f +IRQ_CMD_READIRQ = $00 +IRQ_CMD_ENABLE = $20 +IRQ_CMD_TYPE = $40 +IRQ_CMD_EOI = $ff + +.code + +; void init_irq(); +; mask all IRQs, set all type to edge. +.proc _init_interrupt_controller + ldx #$20 ; enable + ldy #00 + jsr cmd_all + ldx #$40 ; edge type + ldy #$00 + jsr cmd_all + rts + +cmd_all: ; Send the same value to all 32 bytes + txa + add #$20 + sta tmp1 +loop: + txa + sta IRQ_CMD_ADDR + tya + sta IRQ_DAT_ADDR + inx + cpx tmp1 + blt loop + rts +.endproc + + +; void enable_irq(uint8_t type, uint8_t irqnum); +; in A: +.proc _enable_irq + ; Decide which byte we need to modify by dividing by 32 (>> 5) + pha + lsr + lsr + lsr + lsr + lsr ; A is now bytesel + sta tmp2 ; tmp2 is now bytesel + add #IRQ_CMD_ENABLE + sta IRQ_CMD_ADDR + lda IRQ_DAT_ADDR + sta tmp1 + pla + and $07 ; A is now 0-7 + tax + inx ; X is now 1-8 + lda #$01 +L1: dex + beq L2 + asl + bra L1 +L2: pha ; Push bit mask to stack + ora tmp1 ; A is now 1 << (0-7) | enable + sta IRQ_DAT_ADDR + + + lda tmp2 + add #IRQ_CMD_TYPE + sta IRQ_CMD_ADDR + lda IRQ_DAT_ADDR + sta tmp1 + jsr popa ; A is now type + beq bit0 +bit1: ; set `bit` to 1 + pla + ora tmp1 + bra L3 +bit0: ; set `bit` to 0 + pla + eor #$ff + and tmp1 +L3: sta IRQ_DAT_ADDR + rts + +.endproc + +.proc _disable_irq + ; Decide which byte we need to modify by dividing by 32 (>> 5) + pha + lsr + lsr + lsr + lsr + lsr ; A is now bytesel + add #IRQ_CMD_ENABLE + sta IRQ_CMD_ADDR + lda IRQ_DAT_ADDR + sta tmp1 + pla + and $07 ; A is now 0-7 + tax + inx ; X is now 1-8 + lda #$01 +L1: dex + beq L2 + asl + bra L1 +L2: eor #$ff ; Invert to set enable to 0 + and tmp1 ; a is now ~(1 << (0-7)) & enable + sta IRQ_DAT_ADDR + rts +.endproc + +; This should accept irqnum later. +; void send_eoi(); +.proc _send_eoi + lda #IRQ_CMD_EOI + sta IRQ_CMD_ADDR + lda #$1 + sta IRQ_DAT_ADDR + rts +.endproc + diff --git a/sw/kernel/devices/rtc.h b/sw/kernel/devices/rtc.h new file mode 100644 index 0000000..913e7c6 --- /dev/null +++ b/sw/kernel/devices/rtc.h @@ -0,0 +1,16 @@ +#ifndef _RTC_H +#define _RTC_H + +#include + +#define RTC_CMD_ADDR 0xeffe +#define RTC_DAT_ADDR 0xefff + +/* initialize RTC with default values */ +void init_rtc(void); + +/* handle RTC interrupts */ +void handle_rtc(void); + + +#endif \ No newline at end of file diff --git a/sw/kernel/devices/rtc.s b/sw/kernel/devices/rtc.s new file mode 100644 index 0000000..e0778b3 --- /dev/null +++ b/sw/kernel/devices/rtc.s @@ -0,0 +1,78 @@ +.MACPACK generic + +.importzp tmp1 + +.export _init_rtc +.export _handle_rtc + +RTC_CMD = $effe +RTC_DAT = $efff + +RTC_THRESHOLD = $00 +RTC_INCREMENT = $10 +RTC_IRQ_THRESHOLD = $20 +RTC_OUTPUT = $30 +RTC_CONTROL = $30 + +; void init_rtc(void); +; Initialize rtc and generate 50ms interrupts +.proc _init_rtc + lda #RTC_INCREMENT+0 ; Set increment to 1 + sta RTC_CMD + lda #$01 + sta RTC_DAT + lda #RTC_INCREMENT+1 + sta RTC_CMD + lda #$00 + sta RTC_DAT + lda #RTC_INCREMENT+2 + sta RTC_CMD + lda #$00 + sta RTC_DAT + lda #RTC_INCREMENT+3 + sta RTC_CMD + lda #$00 + sta RTC_DAT + + lda #RTC_THRESHOLD+0 ; Set threshold to 4000 ($fa0) + sta RTC_CMD + lda #$a0 + sta RTC_DAT + lda #RTC_THRESHOLD+1 + sta RTC_CMD + lda #$0f + sta RTC_DAT + lda #RTC_THRESHOLD+2 + sta RTC_CMD + lda #$00 + sta RTC_DAT + lda #RTC_THRESHOLD+3 + sta RTC_CMD + lda #$00 + sta RTC_DAT + + lda #RTC_IRQ_THRESHOLD+0 ; Set irq threshold to 50 ($32) + sta RTC_CMD + lda #$32 + sta RTC_DAT + lda #RTC_IRQ_THRESHOLD+1 + sta RTC_CMD + lda #$00 + sta RTC_DAT + lda #RTC_IRQ_THRESHOLD+2 + sta RTC_CMD + lda #$00 + sta RTC_DAT + lda #RTC_IRQ_THRESHOLD+3 + sta RTC_CMD + lda #$00 + sta RTC_DAT + rts + +.endproc + + +.proc _handle_rtc + nop + rti +.endproc \ No newline at end of file diff --git a/sw/kernel/kernel.c b/sw/kernel/kernel.c index ef18f01..50564bf 100644 --- a/sw/kernel/kernel.c +++ b/sw/kernel/kernel.c @@ -1,25 +1,27 @@ #include - -char* longstring = \ -"This is a very long string that is meant to test the loader.\ - We can only load one cluster so far, which means 8 sectors of\ - 512bytes, or a total of 4k. If there was any more data than this,\ - then we would have to traverse the fat to find the next cluster number.\ - This may not be that difficult, but the file will need to be large\ - enough to actually stretch that far. The kernel will probably be\ - that big in the future, but for now when it doesnt really do anything\ - then it can't really be tested."; +#include "devices/interrupt_controller.h" +#include "devices/rtc.h" int main() { - char* string = "this is a shorter string"; - int val = 42; - cputs("Kernel\n"); - cprintf("Kernel printf\n"); - cprintf("Val: %d\n", val); - cprintf("%s", string); - cprintf("Here is a long string: %s\n", longstring); + cputs("Kernel\n"); + + // cputs("Init Paging\n") + // init_paging() + + // cputs("Initialize Interrupts\n"); + // init_interrupts(); + + cputs("Initialize Interrupt Controller\n"); + init_interrupt_controller(); + + cputs("Initialize RTC\n"); + init_rtc(); + + // cputs("Initialize Serial\n"); + // // init_serial(); + // enable_irq(2, IRQ_EDGE); while(1);