mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-11-16 21:23:42 +02:00
initial commit
This commit is contained in:
commit
aee799648f
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
all
|
||||
all.raw.gz
|
||||
report
|
||||
uimage
|
||||
*.o
|
43
Makefile
Normal file
43
Makefile
Normal file
@ -0,0 +1,43 @@
|
||||
load = 0xa0000000
|
||||
|
||||
CXXFLAGS = -Wno-unused-parameter -fno-strict-aliasing -fno-builtin -nostdinc
|
||||
CPPFLAGS = -O5 -Wa,-mips32
|
||||
CROSS = mipsel-linux-gnu-
|
||||
CC = $(CROSS)gcc
|
||||
LD = $(CROSS)ld
|
||||
OBJCOPY = $(CROSS)objcopy
|
||||
OBJDUMP = $(CROSS)objdump
|
||||
|
||||
BUILT_SOURCES = interrupts.cc panic.cc data.cc test.cc
|
||||
|
||||
PYPP = /usr/bin/pypp
|
||||
%.cc: %.ccp kernel.hh
|
||||
$(PYPP) --name $< < $< > $@
|
||||
%.hh: %.hhp
|
||||
$(PYPP) --name $< < $< > $@
|
||||
|
||||
# Transform ':' into ';' so vim doesn't think there are errors.
|
||||
uimage: all.raw.gz Makefile
|
||||
mkimage -A MIPS -O Linux -C gzip -a $(load) -e 0x$(shell /bin/sh -c '$(OBJDUMP) -t all | grep __start$$ | cut -b-8') -n "Shevek's kernel" -d $< $@ | sed -e 's/:/;/g'
|
||||
|
||||
%.o:%.cc Makefile kernel.hh
|
||||
$(CC) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
%.o:%.S Makefile
|
||||
$(CC) $(CPPFLAGS) -DKERNEL_STACK_SIZE=0x2000 -c $< -o $@
|
||||
|
||||
# entry.o must be the first file. For the rest, the order doesn't matter.
|
||||
all: entry.o $(subst .cc,.o,$(BUILT_SOURCES))
|
||||
$(LD) --omagic -Ttext $(load) $^ -o $@
|
||||
|
||||
junk = mdebug.abi32 reginfo comment pdr
|
||||
%.raw: %
|
||||
$(OBJCOPY) -S $(addprefix --remove-section=.,$(junk)) -Obinary $< $@
|
||||
|
||||
%.gz: %
|
||||
gzip < $< > $@
|
||||
|
||||
clean:
|
||||
rm -f all uimage *.o all.raw.gz
|
||||
|
||||
.PHONY: clean
|
8
data.ccp
Normal file
8
data.ccp
Normal file
@ -0,0 +1,8 @@
|
||||
#pypp 0
|
||||
#define EXTERN
|
||||
#include "kernel.hh"
|
||||
|
||||
// This is needed to make gcc happy to compile c++ code without
|
||||
// its standard library.
|
||||
char __gxx_personality_v0[] = "hack";
|
||||
|
230
entry.S
Normal file
230
entry.S
Normal file
@ -0,0 +1,230 @@
|
||||
// The kernel stack.
|
||||
.lcomm kernel_stack, KERNEL_STACK_SIZE
|
||||
|
||||
.globl __start
|
||||
.globl run_idle
|
||||
.set noat
|
||||
.set noreorder
|
||||
|
||||
#define EntryLo0 2
|
||||
#define EntryLo1 3
|
||||
#define EntryHi 10
|
||||
#define Random 1
|
||||
#define Index 0
|
||||
#define PageMask 5
|
||||
#define Wired 6
|
||||
#define Config 16
|
||||
#define Status 12
|
||||
#define Cause 13
|
||||
#define Compare 11
|
||||
|
||||
#define save_regs \
|
||||
sw $k0, -0x180($zero) ;\
|
||||
lw $k0, -0x184($zero) ;\
|
||||
sw $at, 12($k0) ;\
|
||||
sw $k1, 108($k0) ;\
|
||||
lw $k1, -0x180($zero) ;\
|
||||
sw $k1, 104($k0) ;\
|
||||
sw $v0, 16($k0) ;\
|
||||
sw $v1, 20($k0) ;\
|
||||
sw $t0, 40($k0) ;\
|
||||
sw $sp, 84($k0) ;\
|
||||
sw $fp, 88($k0) ;\
|
||||
sw $ra, 92($k0) ;\
|
||||
lw $gp, -12($zero) ;\
|
||||
la $sp, kernel_stack + KERNEL_STACK_SIZE ;\
|
||||
move $a0, $k0 ;\
|
||||
la $ra, kernel_exit ;\
|
||||
bal save_args
|
||||
|
||||
#define restore_regs \
|
||||
lw $v0, 96($k0) ;\
|
||||
lw $v1, 100($k0) ;\
|
||||
mthi $v0 ;\
|
||||
mtlo $v1 ;\
|
||||
lw $v0, 16($k0) ;\
|
||||
lw $v1, 20($k0) ;\
|
||||
lw $a0, 24($k0) ;\
|
||||
lw $a1, 28($k0) ;\
|
||||
lw $a2, 32($k0) ;\
|
||||
lw $a3, 36($k0) ;\
|
||||
lw $t0, 40($k0) ;\
|
||||
lw $t1, 44($k0) ;\
|
||||
lw $t2, 48($k0) ;\
|
||||
lw $t3, 52($k0) ;\
|
||||
lw $t4, 56($k0) ;\
|
||||
lw $t5, 60($k0) ;\
|
||||
lw $t6, 64($k0) ;\
|
||||
lw $t7, 68($k0) ;\
|
||||
lw $t8, 72($k0) ;\
|
||||
lw $t9, 76($k0) ;\
|
||||
lw $gp, 80($k0) ;\
|
||||
lw $sp, 84($k0) ;\
|
||||
lw $fp, 88($k0) ;\
|
||||
lw $ra, 92($k0) ;\
|
||||
lw $at, 12($k0) ;\
|
||||
lw $k1, 104($k0) ;\
|
||||
sw $k1, -0x180($zero) ;\
|
||||
lw $k1, 108($k0) ;\
|
||||
sw $k0, -0x184($zero) ;\
|
||||
lw $k0, -0x180($zero)
|
||||
|
||||
addr_000:
|
||||
// TLB refill
|
||||
// TODO: this should probably be assembly-only for speed reasons
|
||||
|
||||
li $a0, 0xffff0000
|
||||
la $t9, panic
|
||||
jr $t9
|
||||
|
||||
save_regs
|
||||
mfc0 $a1, $EntryHi
|
||||
la $t9, tlb_refill
|
||||
jr $t9
|
||||
nop
|
||||
.fill 0x100 - (. - addr_000)
|
||||
addr_100:
|
||||
// Cache error
|
||||
// TODO
|
||||
|
||||
li $a0, 0xaaaa0000
|
||||
la $t9, panic
|
||||
jr $t9
|
||||
|
||||
save_regs
|
||||
la $t9, cache_error
|
||||
jr $t9
|
||||
nop
|
||||
.fill 0x180 - (. - addr_000)
|
||||
addr_180:
|
||||
// General exception
|
||||
// TODO
|
||||
|
||||
li $a0, 0xaaff0000
|
||||
la $t9, panic
|
||||
jr $t9
|
||||
|
||||
save_regs
|
||||
la $t9, exception
|
||||
jr $t9
|
||||
nop
|
||||
.fill 0x200 - (. - addr_000)
|
||||
addr_200:
|
||||
// Interrupt
|
||||
// TODO
|
||||
|
||||
li $a0, 0x0a0f0000
|
||||
la $t9, panic
|
||||
jr $t9
|
||||
|
||||
save_regs
|
||||
la $t9, interrupt
|
||||
jr $t9
|
||||
nop
|
||||
.fill 0x280 - (. - addr_000)
|
||||
|
||||
// space for save_regs.
|
||||
.word 0
|
||||
.word 0
|
||||
|
||||
start_idle:
|
||||
b .
|
||||
nop
|
||||
.word idle_page
|
||||
.word 0x80000000 // A pointer to the current page.
|
||||
|
||||
__start:
|
||||
bal 1f
|
||||
nop
|
||||
.word _gp
|
||||
// For some reason the disassembler considers everything
|
||||
// after __start non-code until the next label. So I add a label.
|
||||
start_hack:
|
||||
1: lw $gp, 0($ra)
|
||||
|
||||
la $sp, kernel_stack + KERNEL_STACK_SIZE
|
||||
|
||||
// Disable interrupts during bootstrap.
|
||||
mtc0 $zero, $Status
|
||||
|
||||
// TODO: flush cache
|
||||
|
||||
// Set kseg0 cachable.
|
||||
li $k0, 0x3
|
||||
mtc0 $k0, $Config, 0
|
||||
|
||||
// Clear .bss
|
||||
la $a0, _edata
|
||||
la $a1, _end
|
||||
1: sw $zero, 0($a0)
|
||||
bne $a1, $a0, 1b
|
||||
addu $a0, 4
|
||||
|
||||
// Set timer to a defined value
|
||||
mtc0 $zero, $Compare, 0
|
||||
|
||||
// Use the interrupt vector for interrupts
|
||||
li $k0, 1 << 23
|
||||
mtc0 $k0, $Cause
|
||||
|
||||
li $a0, 0xff0a0000
|
||||
la $t9, panic
|
||||
jr $t9
|
||||
|
||||
// clear the tlb, hardwire page 0 to 0xffffffff
|
||||
// and soft-wire it to (0x294 << 20) + (0x290 << 10)
|
||||
// (for the idle task).
|
||||
|
||||
// this address doesn't reach the tlb, so it can't trigger exceptions.
|
||||
mtc0 $zero, $PageMask
|
||||
mtc0 $zero, $EntryLo0
|
||||
mtc0 $zero, $EntryLo1
|
||||
li $t0, 0x80000000
|
||||
// There are 32 tlb entries.
|
||||
addi $a0, $zero, 31
|
||||
1: mtc0 $t0, $EntryHi
|
||||
mtc0 $a0, $Index
|
||||
tlbwi
|
||||
addiu $t0, $t0, 1 << 13
|
||||
bgtz $a0, 1b
|
||||
addiu $a0, $a0, -1
|
||||
|
||||
la $t9, init
|
||||
jalr $t9
|
||||
nop
|
||||
sw $gp, -12($zero)
|
||||
|
||||
save_args:
|
||||
sw $a0, 24($k0)
|
||||
sw $a1, 28($k0)
|
||||
sw $a2, 32($k0)
|
||||
sw $a3, 36($k0)
|
||||
sw $t1, 44($k0)
|
||||
sw $t2, 48($k0)
|
||||
sw $t3, 52($k0)
|
||||
sw $t4, 56($k0)
|
||||
sw $t5, 60($k0)
|
||||
sw $t6, 64($k0)
|
||||
sw $t7, 68($k0)
|
||||
sw $t8, 72($k0)
|
||||
sw $t9, 76($k0)
|
||||
sw $gp, 80($k0)
|
||||
j $ra
|
||||
|
||||
run_idle:
|
||||
move $k0, $a0
|
||||
move $k1, $gp
|
||||
// Prepare enabling interrupts and switching to user mode.
|
||||
// The command is executed in the delay slot of the jump.
|
||||
li $t0, 0xff11
|
||||
// Now it's acceptable to receive an exception. Jump to kuseg,
|
||||
// thereby generating a tlb_refill exception (which is handled).
|
||||
// The address is the virtual address where the idle task is.
|
||||
la $t9, ((0x294 << 20) + (0x290 << 10) + 0x288)
|
||||
jr $t9
|
||||
mtc0 $t0, $Status
|
||||
|
||||
kernel_exit:
|
||||
move $k0, $v0
|
||||
restore_regs
|
||||
eret
|
115
interrupts.ccp
Normal file
115
interrupts.ccp
Normal file
@ -0,0 +1,115 @@
|
||||
#pypp 0
|
||||
#include "kernel.hh"
|
||||
|
||||
// hi and lo cannot be saved in assemply due to space restrictions.
|
||||
#define save_hilo(x) do { __asm__ ("mfhi %0 ; mflo %1" : "=r"((x)->cpu.hi), "=r"((x)->cpu.lo)); } while (0)
|
||||
|
||||
/// A TLB miss has occurred. This should eventually move to entry.S.
|
||||
Thread *tlb_refill (Thread *current, unsigned EntryHi):
|
||||
save_hilo (current)
|
||||
led (false, false)
|
||||
Page ***dir = current->address_space->cpu.directory
|
||||
if !dir:
|
||||
panic (0x00000000, "no page directory for thread")
|
||||
EntryHi >>= 12
|
||||
Page **table = dir[EntryHi >> 10]
|
||||
if !table:
|
||||
panic (0x11111111, "no page table at requested address")
|
||||
EntryHi &= (1 << 10) - 1
|
||||
Page *page0 = table[EntryHi & ~1]
|
||||
Page *page1 = table[EntryHi | 1]
|
||||
if (!(EntryHi & 1) && !page0) || ((EntryHi & 1) && !page1):
|
||||
panic (0x22222222, "no page mapped at requested address")
|
||||
unsigned low0, low1
|
||||
if page0:
|
||||
low0 = page0->physical | 0x18 | 0x4 | 0x2
|
||||
else
|
||||
low0 = 0
|
||||
if page1:
|
||||
low1 = page1->physical | 0x18 | 0x4 | 0x2
|
||||
else
|
||||
low1 = 0
|
||||
__asm__ ("mtc0 %0, $2; mtc0 %1, $3; tlbwr" :: "r"(low0), "r"(low1))
|
||||
return current
|
||||
|
||||
/// An interrupt which is not an exception has occurred.
|
||||
Thread *interrupt (Thread *current):
|
||||
save_hilo (current)
|
||||
led (false, false)
|
||||
return current
|
||||
|
||||
/// A general exception has occurred.
|
||||
Thread *exception (Thread *current):
|
||||
save_hilo (current)
|
||||
panic (0xdeadbeaf, "exception")
|
||||
return current
|
||||
|
||||
/// There's a cache error. Big trouble. Probably not worth trying to recover.
|
||||
Thread *cache_error (Thread *current):
|
||||
save_hilo (current)
|
||||
panic (0x33333333, "cache error")
|
||||
return current
|
||||
|
||||
static void init_idle ():
|
||||
// initialize idle task as if it is currently running.
|
||||
idle.size = sizeof (Thread)
|
||||
idle.prev = NULL
|
||||
idle.next = NULL
|
||||
idle.thread_prev = NULL
|
||||
idle.thread_next = NULL
|
||||
idle.schedule_prev = NULL
|
||||
idle.schedule_next = NULL
|
||||
idle.address_space = &idle_memory
|
||||
// initialize idle_memory.
|
||||
idle_memory.size = sizeof (Memory)
|
||||
idle_memory.prev = NULL
|
||||
idle_memory.next = NULL
|
||||
idle_memory.memory_prev = NULL
|
||||
idle_memory.memory_next = NULL
|
||||
idle_memory.pages = &idle_page
|
||||
idle_memory.threads = &idle
|
||||
idle_memory.memories = NULL
|
||||
idle_memory.limit = 0
|
||||
idle_memory.used = 0
|
||||
idle_memory.cpu.directory = (Page ***)0x80000000
|
||||
idle_memory.cpu.asid = 0
|
||||
// initialize idle_page
|
||||
idle_page.size = sizeof (Page)
|
||||
idle_page.prev = NULL
|
||||
idle_page.next = NULL
|
||||
idle_page.page_prev = NULL
|
||||
idle_page.page_next = NULL
|
||||
// Not an error, this is really physical page 0.
|
||||
idle_page.physical = 0
|
||||
|
||||
/// Initialize the kernel, finish by falling into the idle task.
|
||||
extern unsigned _end
|
||||
void init ():
|
||||
// Initialize kernel variables to empty.
|
||||
sleepers = NULL
|
||||
runners = NULL
|
||||
zero_pages = NULL
|
||||
// Fill junk pages with all memory not currently used.
|
||||
junk_pages = (FreePage *)(((unsigned)&_end + (1 << 12) - 1) & ~((1 << 12) - 1))
|
||||
FreePage *p, *next
|
||||
unsigned count = 1
|
||||
for p = junk_pages; (unsigned)next - 0x80000000 < (1 << 27); p = next, next = (FreePage *)((unsigned)p + 1 << 12):
|
||||
p->next = next
|
||||
++count
|
||||
p->next = NULL
|
||||
// initialize everything about the idle task.
|
||||
init_idle ()
|
||||
// initialize top_memory.
|
||||
top_memory.size = sizeof (Memory)
|
||||
top_memory.prev = NULL
|
||||
top_memory.next = NULL
|
||||
top_memory.memory_prev = NULL
|
||||
top_memory.memory_next = NULL
|
||||
top_memory.pages = NULL
|
||||
top_memory.threads = NULL
|
||||
top_memory.memories = NULL
|
||||
top_memory.limit = count
|
||||
top_memory.used = 0
|
||||
top_memory.cpu.directory = NULL
|
||||
top_memory.cpu.asid = 0
|
||||
// TOOO: set up initial threads.
|
75
kernel.hhp
Normal file
75
kernel.hhp
Normal file
@ -0,0 +1,75 @@
|
||||
#pypp 0
|
||||
#ifndef _KERNEL_HH
|
||||
#define _KERNEL_HH
|
||||
|
||||
#define NULL 0
|
||||
|
||||
class Object
|
||||
class Page
|
||||
class Thread
|
||||
class Memory
|
||||
|
||||
struct Object:
|
||||
unsigned size
|
||||
Object *prev, *next
|
||||
|
||||
struct Page : public Object:
|
||||
Page *page_prev, *page_next
|
||||
unsigned physical
|
||||
|
||||
struct Thread : public Object:
|
||||
struct Cpu:
|
||||
unsigned at, v0, v1, a0, a1, a2, a3
|
||||
unsigned t0, t1, t2, t3, t4, t5, t6, t7, t8, t9
|
||||
unsigned gp, sp, fp, ra, hi, lo, k0, k1, pc
|
||||
Cpu cpu
|
||||
Thread *thread_prev, *thread_next
|
||||
Thread *schedule_prev, *schedule_next
|
||||
Memory *address_space
|
||||
|
||||
struct Memory : public Object:
|
||||
Memory *memory_prev, *memory_next
|
||||
Page *pages
|
||||
Thread *threads
|
||||
Memory *memories
|
||||
unsigned limit, used
|
||||
struct Cpu:
|
||||
unsigned asid
|
||||
Page ***directory
|
||||
Cpu cpu
|
||||
|
||||
// Functions which can be called from assembly must not be mangled.
|
||||
extern "C":
|
||||
// Kernel entry points, called from entry.S.
|
||||
void init ()
|
||||
Thread *interrupt (Thread *current)
|
||||
Thread *cache_error (Thread *current)
|
||||
Thread *exception (Thread *current)
|
||||
|
||||
// tlb stuff. tlb_refill is also an entry point.
|
||||
void tlb_setup ()
|
||||
Thread *tlb_refill (Thread *current, unsigned EntryHi)
|
||||
|
||||
// Start running the idle task for the first time.
|
||||
void run_idle (Thread *self)
|
||||
|
||||
// Panic. n is sent over caps led. message is currently ignored.
|
||||
void panic (unsigned n, char const *message)
|
||||
// Debug: switch caps led
|
||||
void led (bool on, bool tick)
|
||||
|
||||
#ifndef EXTERN
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
struct FreePage:
|
||||
FreePage *next
|
||||
|
||||
EXTERN FreePage *zero_pages, *junk_pages
|
||||
EXTERN Memory top_memory
|
||||
EXTERN Thread *sleepers, *runners
|
||||
EXTERN Thread idle
|
||||
EXTERN Memory idle_memory
|
||||
EXTERN Page idle_page
|
||||
|
||||
#endif
|
10
panic.ccp
Normal file
10
panic.ccp
Normal file
@ -0,0 +1,10 @@
|
||||
#pypp 0
|
||||
#include "kernel.hh"
|
||||
|
||||
void panic (unsigned n, char const *message):
|
||||
while (1):
|
||||
for unsigned bit = 0x80000000; bit; bit >>= 1:
|
||||
for int i = 0; i < 60000; ++i:
|
||||
led (n & bit, i > 20000 && i < 40000)
|
||||
for int i = 0; i < 100000; ++i:
|
||||
led (false, false)
|
24
start.S
Normal file
24
start.S
Normal file
@ -0,0 +1,24 @@
|
||||
.set noreorder
|
||||
.globl __start
|
||||
.globl kernel_stack
|
||||
__start:
|
||||
bal 1f
|
||||
nop
|
||||
.word _gp
|
||||
1: lw $gp, 0($ra)
|
||||
nop
|
||||
|
||||
la $a0, _edata
|
||||
la $a1, _end
|
||||
1: sw $zero, 0($a0)
|
||||
bne $a1, $a0, 1b
|
||||
addu $a0, 4
|
||||
|
||||
la $sp, kernel_stack + KERNEL_STACK_SIZE
|
||||
|
||||
bal kernel_entry
|
||||
nop
|
||||
b .
|
||||
nop
|
||||
|
||||
.lcomm kernel_stack, KERNEL_STACK_SIZE
|
74
test.ccp
Normal file
74
test.ccp
Normal file
@ -0,0 +1,74 @@
|
||||
#pypp 0
|
||||
#include "kernel.hh"
|
||||
|
||||
#define REG32(addr) *((volatile unsigned int *)(addr))
|
||||
|
||||
#define GPIO_BASE 0xB0010000
|
||||
|
||||
#define __gpio_port_data(p) ( REG_GPIO_GPDR(p) )
|
||||
|
||||
#define GPIO_GPSLR(n) (GPIO_BASE + (0x10 + (n)*0x30))
|
||||
#define GPIO_GPSUR(n) (GPIO_BASE + (0x14 + (n)*0x30))
|
||||
#define GPIO_GPFLR(n) (GPIO_BASE + (0x18 + (n)*0x30))
|
||||
#define GPIO_GPFUR(n) (GPIO_BASE + (0x1c + (n)*0x30))
|
||||
|
||||
#define GPIO_GPDR(n) (GPIO_BASE + (0x00 + (n)*0x30))
|
||||
|
||||
#define REG_GPIO_GPSLR(n) REG32(GPIO_GPSLR((n)))
|
||||
#define REG_GPIO_GPSUR(n) REG32(GPIO_GPSUR((n)))
|
||||
#define REG_GPIO_GPFLR(n) REG32(GPIO_GPFLR((n)))
|
||||
#define REG_GPIO_GPFUR(n) REG32(GPIO_GPFUR((n)))
|
||||
|
||||
#define REG_GPIO_GPDR(n) REG32(GPIO_GPDR((n)))
|
||||
|
||||
static void __gpio_port_as_gpiofn (unsigned p, unsigned o, unsigned fn):
|
||||
unsigned int tmp;
|
||||
if o < 16:
|
||||
tmp = REG_GPIO_GPSLR(p)
|
||||
tmp &= ~(3 << ((o) << 1))
|
||||
REG_GPIO_GPSLR(p) = tmp
|
||||
tmp = REG_GPIO_GPFLR(p)
|
||||
tmp &= ~(3 << ((o) << 1))
|
||||
tmp |= fn << ((o) << 1)
|
||||
REG_GPIO_GPFLR(p) = tmp
|
||||
else:
|
||||
tmp = REG_GPIO_GPSUR(p)
|
||||
tmp &= ~(3 << (((o) - 16) << 1))
|
||||
REG_GPIO_GPSUR(p) = tmp
|
||||
tmp = REG_GPIO_GPFUR(p)
|
||||
tmp &= ~(3 << (((o) - 16) << 1))
|
||||
tmp |= fn << (((o) - 16) << 1)
|
||||
REG_GPIO_GPFUR(p) = tmp
|
||||
|
||||
static void __gpio_port_as_output (unsigned p, unsigned o):
|
||||
__gpio_port_as_gpiofn (p, o, 1)
|
||||
static void __gpio_port_as_input (unsigned p, unsigned o):
|
||||
__gpio_port_as_gpiofn (p, o, 0)
|
||||
static void __gpio_as_output (unsigned n):
|
||||
__gpio_port_as_output(n / 32, n % 32)
|
||||
static void __gpio_as_input (unsigned n):
|
||||
__gpio_port_as_input(n / 32, n % 32)
|
||||
static void __gpio_set_pin (unsigned n):
|
||||
__gpio_port_data (n / 32) |= (1 << (n % 32))
|
||||
static void __gpio_clear_pin (unsigned n):
|
||||
__gpio_port_data (n / 32) &= ~(1 << (n % 32))
|
||||
|
||||
#define CAPSLOCKLED_IO 27
|
||||
#define NUMLOCKLED_IO 86
|
||||
#define NETWORK_IO 9
|
||||
#define LIGHT 105
|
||||
|
||||
void led (bool on, bool tick):
|
||||
__gpio_as_output (CAPSLOCKLED_IO)
|
||||
__gpio_as_output (NUMLOCKLED_IO)
|
||||
__gpio_as_output (NETWORK_IO)
|
||||
if on:
|
||||
__gpio_set_pin (NUMLOCKLED_IO)
|
||||
__gpio_clear_pin (CAPSLOCKLED_IO)
|
||||
else:
|
||||
__gpio_set_pin (CAPSLOCKLED_IO)
|
||||
__gpio_clear_pin (NUMLOCKLED_IO)
|
||||
if tick:
|
||||
__gpio_clear_pin (NETWORK_IO)
|
||||
else:
|
||||
__gpio_set_pin (NETWORK_IO)
|
Loading…
Reference in New Issue
Block a user