Yep, that's exactly what I would expect.
The 'end' statement is only for the compiler to know there is no more code in the file to look at and does nothing for the code running in the PIC. After the last Dlay 30000000 line the PIC executes the next instruction in memory and since the program code space was erased this means that the next instruction is a 'nop'. So is the instruction after that and after that until the program counter gets to the end of the code space. The program counter then wraps from the highest address to zero and starts executing the code from the beginning. Thus it keeps looping.
Add these lines before the 'end' statement:
wait_here
goto wait_here ; loop forever
Or this line
goto $ ; loop forever
Both will keep looping to the same line and thus appear to have halted.
Here is a good tutorial on PIC code:
http://www.gooligum.com.au/tut_baseline.html