From 910f42c1f2788ed61add61aaea42c720f82d2a17 Mon Sep 17 00:00:00 2001 From: dwelch Date: Tue, 27 Nov 2012 17:12:25 -0500 Subject: [PATCH] adding blinker07 another interrupt demonstration using the system timer --- blinker07/Makefile | 40 ++++++ blinker07/README | 304 ++++++++++++++++++++++++++++++++++++++++++ blinker07/blinker07.c | 204 ++++++++++++++++++++++++++++ blinker07/memmap | 11 ++ blinker07/start.s | 39 ++++++ blinker07/vectors.s | 115 ++++++++++++++++ 6 files changed, 713 insertions(+) create mode 100644 blinker07/Makefile create mode 100644 blinker07/README create mode 100644 blinker07/blinker07.c create mode 100644 blinker07/memmap create mode 100644 blinker07/start.s create mode 100644 blinker07/vectors.s diff --git a/blinker07/Makefile b/blinker07/Makefile new file mode 100644 index 0000000..03de569 --- /dev/null +++ b/blinker07/Makefile @@ -0,0 +1,40 @@ + +ARMGNU ?= arm-none-eabi + +COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding + +gcc : blinker07.hex blinker07.bin + +all : gcc + +clean : + rm -f *.o + rm -f *.bin + rm -f *.hex + rm -f *.elf + rm -f *.list + rm -f *.img + rm -f *.bc + rm -f *.clang.opt.s + +vectors.o : vectors.s + $(ARMGNU)-as vectors.s -o vectors.o + +blinker07.o : blinker07.c + $(ARMGNU)-gcc $(COPS) -c blinker07.c -o blinker07.o + +blinker07.elf : memmap vectors.o blinker07.o + $(ARMGNU)-ld vectors.o blinker07.o -T memmap -o blinker07.elf + $(ARMGNU)-objdump -D blinker07.elf > blinker07.list + +blinker07.bin : blinker07.elf + $(ARMGNU)-objcopy blinker07.elf -O binary blinker07.bin + +blinker07.hex : blinker07.elf + $(ARMGNU)-objcopy blinker07.elf -O ihex blinker07.hex + + + + + + diff --git a/blinker07/README b/blinker07/README new file mode 100644 index 0000000..875fbee --- /dev/null +++ b/blinker07/README @@ -0,0 +1,304 @@ + +See the top level README file for more information on documentation +and how to run these programs. + +Derived from prior blinker examples and uart04, this is an example using +the system timer interrupt. + +For starters perhaps the ARM should not be using these interrupts, they +are only halfway documented. As of this writing it appears that the +gpu is using counter match 0 and 2, so cm1 and 3 are not being used. +This example uses CM1. + +The documentation says that the status flag will assert when the lower +half of the system timer matches the counter match register. It also +says that the software interrupt service routine should change the +match register. Basically software has to keep putting the counter match +out in front of the timer. If you want an interrupt every 1234 counts +then each interrupt you need to add 1234 to the count match register, +the hardware wont do it for you. The manual says in one place that +you write zero and read dont care, but elsewhere says that you write +one to clear the status bits. The write one to clear appears to be +how it works. So when the counter status match flag is set then we +write a 1 to that bit location in that register (write a 2 to counter +status to clear counter match 1). + +To figure out what interrupt line this was tied to, using some uart code +so I could print stuff out and see it I enabled all interrupts, wrote +0xFFFFFFFF to both interrupt enable registers. Then read the current +count, added 0x00400000 and wrote CM1 with that value. Then went into +an infinite loop printing the interrupt status registers. By doing this +both with CM1 and CM3 I figured out that irq 1 goes with CM1 and irq3 goes +with CM3. + +The last bit of information required is how do you clear the interrupt. +When writing the 1 to the counter status register to clear the match +flag, that also clears the pending interrupt in the interrupt status +register. + +This example demonstrates multiple things. First it uses generic polling +of the system timer to blink the led on and off three times. Then it +uses the counter match register and the status flag to time four on/off +blink cycles. The blink rate is twice the speed of the first three. +Next it enables the interrupt in the interrupt controller, not to the ARM, +not yet, just to the chips interrupt controller. When enabled the interrupt +line reflects the counter status match flag, so basically the next three +blinks are done the same way as the prior four except it is sampling +the match status using the interrupt controller status. These blinks +are slower than the prior four. The last thing it does is enable the +interrupt to the ARM. And uses the interrupt to indicate the counter +match hit. The ARM code then computes a new counter match and waits +for the next hit. This loop happens to make the blink rate slower each +time so you can perhaps tell you are in that loop. Eventually the +timer interval will be so large that it goes back to a small number +and starts blinking faster then progressively slower. This should take +a long while to happen. + +You realy need to get the ARM ARM (ARM Architectural Reference Manual) +for this architecture and or get the oldest architecture on their web +site which is currently the ARMv5 ARM (it includes the ARMv4 as well, +this is the original ARM ARM before it was split into multiple documents). +In the ARM ARM it describes the exception process in more detail. + +The short answer is that starting at address 0x00000000 in ARM address +space there are a number of exception vectors. Unlike many other processors +these are not addresses for the handers these are instructions that get +executed. Being one word in size, you probably want those to be +branch instructions or ldr pc instructions. + +The way I am using the Raspberry Pi is letting the gpu load the arm +program (kernel.img) at address 0x8000. The gpu then puts an instruction +at address zero (and some other stuff between 0x0000 and 0x8000 for linux) +the lets the ARM boot. I am not linux so dont care about the stuff between +0x0000 and 0x8000. I do need to change at least the memory location for +the interrupt handler so that when the interrupt occurs the ARM executes +my handler. + +Using basic ARM knowledge and letting the assembler and compiler do some +of the work I create an exception table at 0x8000 in such a way that +it can be copied to 0x0000 and still work. + +Looking at the beginning of vectors.s which for any of my programs to +work need to be compiled and linked such that _start is at address 0x8000, +the first thing in the .bin file. + +The assembly code uses .word to allocate 32 bit memory locations which +will each hold an address to a handler. + +reset_handler: .word reset + +reset_handler is the label. .word means I want to allocate 32 bit items +and reset is the name of another label. The assembler does some of the +work then the linker does the rest to determine what the final value +of the reset labels ARM address is. That address is placed in the binary +in this allocated space. Which can be seen in the disassembly: + +00008020 : + 8020: 00008040 andeq r8, r0, r0, asr #32 + +... + +00008040 : + 8040: e3a00902 mov r0, #32768 ; 0x8000 + + +Here is where the ARM knowledge, or at least more of it, comes in. +Although the disassembly shows that the instruction is loading the +value 0x8020 or 0x8040 or whatever. The instruction is actually loading +a pc relative address. You can partially tell this from the disassembly +[pc,#24] means pc value plus 24 (0x18), it doesnt mean 0x8020, etc. + +00008000 <_start>: + 8000: e59ff018 ldr pc, [pc, #24] ; 8020 + 8004: e59ff018 ldr pc, [pc, #24] ; 8024 + 8008: e59ff018 ldr pc, [pc, #24] ; 8028 + +More ARM knowledge. From a programmers perspective the PC is two +instructions ahead. You are in arm mode when you hit these exceptions +so the PC is 8 bytes ahead so at address 0x8000 the PC is 0x8008 when +you execute that instruction add 24 (0x18) to the PC, 0x8008+0x18 = 0x8020 +and you get the address 0x8020. the instructin is now ldr pc,[0x8020] +Memory location 0x8020 holds the value 0x8040 which is what is loaded into +the program counter and we begin executing at 0x8040 which is the reset +handler, that is what we wanted. + +here is the tricky bit. What if we copied both the reset handler stuff +and the list of addresses, all of it, to address 0x0000? (at runtime +after all the compiling and linking were long over and we are running). + +Instead of the addresses being this: + + 8018: e59ff018 ldr pc, [pc, #24] ; 8038 + ... +00008038 : + 8038: 000080c4 andeq r8, r0, r4, asr #1 + +The copy of the data/instructions would now have these addresses: + + 0018: e59ff018 ldr pc, [pc, #24] + ... +00000038 : + 0038: 000080c4 andeq r8, r0, r4, asr #1 + +When the interrupt occurs the ARM runs the instruction at address 0x0018 +which says to take the value of the PC (two ahead remember so the pc is) +0x20 add 24 (which is 0x18) giving 0x0038 as the address to read from. +It reads 0x80C4 and loads that into the program counter so that the +next instruction executed is the one at 0x80C4. Which is where our +interrupt handler really is. + +Basically this is some position independent code with some absolute +addresses for the handlers, the address stuff is done by the assembler +and linker so we dont have to. + + +These instructions right after reset perform the copy of instructions and +data from where our program was loaded and started (0x8000) to where we +need the exception table (0x0000). + + mov r0,#0x8000 + mov r1,#0x0000 + ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9} + stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9} + ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9} + stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9} + +ldmia means load multiple. the IA means increment after so what it does +is using the value in r0 as an address (when executing the first of the +two ldmia instructions r0 is 0x8000) so it loads 8 words starting at +0x8000 into register r2 through r9. Then if there is an exclamation point +after the register (which there is) then it modifies that register to +point to the next word after the last one we loaded so we read 8 words +or 32 bytes at address 0x8000 so the last thing it does (increment after +the load) is add 0x20 and save so r0 is now 0x8020. + +stmia is like the load but a store, r1 starts off as 0x0000 so it stores +those 8 words from 0x8000 to 0x0000, then it address 0x20 to r1. + +so the second ldmia is going to read 8 more words from 0x8020 and the +second stmia is going to write those words to 0x0020. + +The second ldm and stm do not have to have the exclamation point as we dont +care about r0 and r1, which means they dont need the ia. The ia part +of the instruction is an either or thing either you decrement before you +use the address or you increment after, one bit in the instruction encoding +the exclamation point is a separate bit in the instruction that enables +or disables the saving of that value to the base register. So if you +were to do this: + + ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9} + stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9} + ldm r0,{r2,r3,r4,r5,r6,r7,r8,r9} + stm r1,{r2,r3,r4,r5,r6,r7,r8,r9} + +the assembler is likely going to pick ldmia or ldmdb and when you +then disassemble it might look like this: + + ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9} + stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9} + ldmdb r0,{r2,r3,r4,r5,r6,r7,r8,r9} + stmdb r1,{r2,r3,r4,r5,r6,r7,r8,r9} + +it was easy to cut and paste the two lines as is, and if I wanted to +cut and paste more sets to copy more data it is easy. So I left that +extra info on those latter instructions even though I am not using them. + +So what those first 6 instructions did was to basicaly copy 0x40 bytes +from 0x8000 to 0x0000. Since these are very early in the boot we are +not using register r2 to r9 so that made it easy to use them as scratch +registers. If we had waited to copy the 0x40 bytes until later a loop +or some other way of copying that data likely would have happened since +many of those registers my be used by other code. + +Note that the Cortex-M processors from arm which only execute in thumb +mode, cannot execute ARM mode instructions boot differently, have different +exception tables. The Cortex-M processors have addresses not instructions +in the table and each flavor of Cortex-M or worse implementation has +different definitions for each of those entries. The first few are +the same then it diverges and they can have hundreds of entries in the +vector table. The classic ARM table though has not varied for many +flavors of ARM cores and good or bad all interrupts funnel into the +same handler. (or handlers if you count the fiq). + +The classic ARM design also has separate stack pointers for each mode. +Interrupt is a mode, when you get an interrupt you switch from whatever +mode you were in (service/super user) to interrupt mode, which means +you are using a different stack pointer. this is all described in words +and pictures in the ARM ARM. This means that if we are going to support +interrupts not only do we need to set our application stack pointer but +also need to set aside some memory for the interrupt stack and point +the interrupt stack pointer to it. how do you change the interrupt stack +pointer if you are not in interrupt mode? well you have to be in interrupt +mode. How do you get into interrupt mode? Well you modify the cpsr +which contains the mode bits and that magically changes you to that mode. +You can do this from any mode to any mode except from user mode, you cant +get out of user mode by changing the bits. We are not in user mode on +boot and never swtich to it in any of my examples do we dont have to +worry about getting out of it (normally you use an svc/swi instruction and +have a software interrupt handler that does things that are protected +from user mode). So the next bit of code after copying the exception +handler stuff switches into irq mode and fiq mode and sets their +stack pointers (fiq in case you want to experience that mode, mostly +the same as irq, you have another bank of registers so you dont +have to preserve the system registers, making the handler a little faster +as in fast irq (fiq), I do not demonstrate fiq here). + + + ;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) + mov r0,#0xD2 + msr cpsr_c,r0 + mov sp,#0x8000 + + ;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) + mov r0,#0xD1 + msr cpsr_c,r0 + mov sp,#0x4000 + + ;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) + mov r0,#0xD3 + msr cpsr_c,r0 + mov sp,#0x8000000 + + ;@ SVC MODE, IRQ ENABLED, FIQ DIS + ;@mov r0,#0x53 + ;@msr cpsr_c, r0 + + +the cpsr is also where you enable or disable the arm interrupt and fast +interrupt. we want to start off with interrupts disabled so when +switching back to SVC mode we also make sure that they are disabled. + +So the irq stack starts at 0x8000 (first location is 0x7FFC) and the fiq +stack is at 0x4000 (0x3FFC). If you have re-compiled this program or +modified your config.txt to have the gpu load you say at address 0x0000 +then these stacks may collide with your program and you need to move +them. Likewise I have put the SVC stack at 0x80000000 (0x7FFFFFFC) and +if you are using that memory you need to move that as well. Bare metal +memory management is part of bare metal programming. YOU decide where +things are and either hard code them in your code or linker script or +indirectly through the linker script. + +The last thing I am going to say about the interrupt handler is that I +made it pretty stupid and mostly in C. + +irq: + push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + bl c_irq_handler + pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + subs pc,lr,#4 + +When you read the ARM ARM you will see that the proper way to return +from an interrupt is using a subs pc,lr,#4. Since you interrupted +application code which was likely using some most of the registers you +need to preserve those registers, in particular the link register, lr. +So what my assembly wrapper does is preserve all the registers call a +C function, upon return from that C function restore the registers then +return from interrupt. Just like any other textbook interrupt handler. + +You need to remember and understand that C code in an interrupt handler +needs to be lean and mean, get in get out dont mess around. This example +simply modifies a global variable (has to be declared as volatile to +be shared properly between the handler and the rest of the app code) and +the application detects that to know the interrupt happend. that adds +a latency but it is okay since our eyes will not see that difference +in the led blinks. diff --git a/blinker07/blinker07.c b/blinker07/blinker07.c new file mode 100644 index 0000000..388d239 --- /dev/null +++ b/blinker07/blinker07.c @@ -0,0 +1,204 @@ + +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- + +// The raspberry pi firmware at the time this was written defaults +// loading at address 0x8000. Although this bootloader could easily +// load at 0x0000, it loads at 0x8000 so that the same binaries built +// for the SD card work with this bootloader. Change the ARMBASE +// below to use a different location. + +#define ARMBASE 0x8000 +#define CS 0x20003000 +#define CLO 0x20003004 +#define C0 0x2000300C +#define C1 0x20003010 +#define C2 0x20003014 +#define C3 0x20003018 + +#define GPFSEL1 0x20200004 +#define GPSET0 0x2020001C +#define GPCLR0 0x20200028 + +extern void PUT32 ( unsigned int, unsigned int ); +extern unsigned int GET32 ( unsigned int ); + + +extern void enable_irq ( void ); + +//------------------------------------------------------------------------ +volatile unsigned int irq_counter; +//------------------------------------------------------------------------- +void c_irq_handler ( void ) +{ + irq_counter++; + PUT32(CS,2); +} +//------------------------------------------------------------------------ +int notmain ( void ) +{ + unsigned int ra; + unsigned int rb; + unsigned int rc; + unsigned int rx; + unsigned int interval; + + //make gpio pin tied to the led an output + ra=GET32(GPFSEL1); + ra&=~(7<<18); + ra|=1<<18; + PUT32(GPFSEL1,ra); + PUT32(GPSET0,1<<16); //led off +// PUT32(GPCLR0,1<<16); //led on + +if(1) +{ + //just poll the system timer counter itself and use that to + //measure time + + interval=0x00200000; + + rx=GET32(CLO); + for(rb=0;rb<3;rb++) + { + while(1) + { + ra=GET32(CLO); + rc=ra-rx; + if(rc>=interval) break; + } + rx+=interval; + PUT32(GPCLR0,1<<16); //led on + while(1) + { + ra=GET32(CLO); + rc=ra-rx; + if(rc>=interval) break; + } + rx+=interval; + PUT32(GPSET0,1<<16); //led off + } +} + +if(1) +{ + //use the counter match and the counter match flag in the counter + //status register + + interval=0x00100000; + rx=GET32(CLO); + rx+=interval; + PUT32(C1,rx); + PUT32(CS,2); + for(rb=0;rb<4;rb++) + { + while(1) + { + ra=GET32(CS); + if(ra&2) break; + } + rx+=interval; + PUT32(C1,rx); + PUT32(CS,2); + + PUT32(GPCLR0,1<<16); //led on + + while(1) + { + ra=GET32(CS); + if(ra&2) break; + } + rx+=interval; + PUT32(C1,rx); + PUT32(CS,2); + + PUT32(GPSET0,1<<16); //led off + } +} + +if(1) +{ + //poll the interrupt status + interval=0x00200000; + rx=GET32(CLO); + rx+=interval; + PUT32(C1,rx); + PUT32(CS,2); + PUT32(0x2000B210,0x00000002); + for(rb=0;rb<3;rb++) + { + while(1) + { + ra=GET32(0x2000B204); + if(ra&2) + { + rx+=interval; + PUT32(C1,rx); + PUT32(CS,2); + break; + } + } + PUT32(GPCLR0,1<<16); //led on + while(1) + { + ra=GET32(0x2000B204); + if(ra&2) + { + rx+=interval; + PUT32(C1,rx); + PUT32(CS,2); + break; + } + } + PUT32(GPSET0,1<<16); //led off + } +} + +//rely on the interrupt to measure time. + + irq_counter=0; + ra=irq_counter; + interval=0x00080000; + rx=GET32(CLO); + rx+=interval; + PUT32(C1,rx); + PUT32(CS,2); + PUT32(0x2000B210,0x00000002); + enable_irq(); + while(1) + { + while(irq_counter==ra) continue; + ra=irq_counter; + rx+=interval; + PUT32(C1,rx); + + PUT32(GPCLR0,1<<16); //led on + + while(irq_counter==ra) continue; + ra=irq_counter; + rx+=interval; + PUT32(C1,rx); + + PUT32(GPSET0,1<<16); //led off + interval+=0x10000; + if(interval==0) interval=0x00080000; + } + + + return(0); +} +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// +// Copyright (c) 2012 David Welch dwelch@dwelch.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//------------------------------------------------------------------------- diff --git a/blinker07/memmap b/blinker07/memmap new file mode 100644 index 0000000..2dab497 --- /dev/null +++ b/blinker07/memmap @@ -0,0 +1,11 @@ + +MEMORY +{ + ram : ORIGIN = 0x8000, LENGTH = 0x1000 +} + +SECTIONS +{ + .text : { *(.text*) } > ram + .bss : { *(.bss*) } > ram +} diff --git a/blinker07/start.s b/blinker07/start.s new file mode 100644 index 0000000..42c8b70 --- /dev/null +++ b/blinker07/start.s @@ -0,0 +1,39 @@ + +;@------------------------------------------------------------------------- +;@------------------------------------------------------------------------- + +.globl _start +_start: + mov sp,#0x8000 + bl notmain +hang: b hang + +.globl PUT32 +PUT32: + str r1,[r0] + bx lr + +.globl GET32 +GET32: + ldr r0,[r0] + bx lr + +.globl dummy +dummy: + bx lr + +;@------------------------------------------------------------------------- +;@------------------------------------------------------------------------- + + +;@------------------------------------------------------------------------- +;@ +;@ Copyright (c) 2012 David Welch dwelch@dwelch.com +;@ +;@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +;@ +;@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +;@ +;@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +;@ +;@------------------------------------------------------------------------- diff --git a/blinker07/vectors.s b/blinker07/vectors.s new file mode 100644 index 0000000..78765b9 --- /dev/null +++ b/blinker07/vectors.s @@ -0,0 +1,115 @@ + +;@------------------------------------------------------------------------- +;@------------------------------------------------------------------------- + +.globl _start +_start: + ldr pc,reset_handler + ldr pc,undefined_handler + ldr pc,swi_handler + ldr pc,prefetch_handler + ldr pc,data_handler + ldr pc,unused_handler + ldr pc,irq_handler + ldr pc,fiq_handler +reset_handler: .word reset +undefined_handler: .word hang +swi_handler: .word hang +prefetch_handler: .word hang +data_handler: .word hang +unused_handler: .word hang +irq_handler: .word irq +fiq_handler: .word hang + +reset: + mov r0,#0x8000 + mov r1,#0x0000 + ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9} + stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9} + ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9} + stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9} + + ;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) + mov r0,#0xD2 + msr cpsr_c,r0 + mov sp,#0x8000 + + ;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) + mov r0,#0xD1 + msr cpsr_c,r0 + mov sp,#0x4000 + + ;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) + mov r0,#0xD3 + msr cpsr_c,r0 + mov sp,#0x8000000 + + ;@ SVC MODE, IRQ ENABLED, FIQ DIS + ;@mov r0,#0x53 + ;@msr cpsr_c, r0 + + bl notmain +hang: b hang + +.globl PUT32 +PUT32: + str r1,[r0] + bx lr + +.globl PUT16 +PUT16: + strh r1,[r0] + bx lr + +.globl PUT8 +PUT8: + strb r1,[r0] + bx lr + +.globl GET32 +GET32: + ldr r0,[r0] + bx lr + +.globl GETPC +GETPC: + mov r0,lr + bx lr + +.globl BRANCHTO +BRANCHTO: + bx r0 + +.globl dummy +dummy: + bx lr + +.globl enable_irq +enable_irq: + mrs r0,cpsr + bic r0,r0,#0x80 + msr cpsr_c,r0 + bx lr + +irq: + push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + bl c_irq_handler + pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + subs pc,lr,#4 + + +;@------------------------------------------------------------------------- +;@------------------------------------------------------------------------- + + +;@------------------------------------------------------------------------- +;@ +;@ Copyright (c) 2012 David Welch dwelch@dwelch.com +;@ +;@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +;@ +;@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +;@ +;@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +;@ +;@-------------------------------------------------------------------------