3... 2... 1... Crash off!

Perl on Linux, 17 bytes

I thought it might be interesting to create a program that doesn't maintain any state itself, and doesn't modify its own source code; in other words, it actually checks how often it's been run by asking the OS. The question says "Any facilities used for storage may be assumed to be empty before the first execution of the program.", and thus we'd want to start from a completely blank OS. As such, I'd better explain just how you go about doing that, as otherwise testing the program is difficult.

There are actually two ways to set up for running the program. Either way, the minimal OS for the purpose would be running nothing but a shell (to enable us to run Perl in turn), and the simplest possible shell at that (so that it doesn't do anything fancy that makes the OS non-blank). /bin/dash is a good choice here, as it was intended as a minimal shell for system recovery. Then, we need to start Linux in such a way that it's running nothing but dash. We can either do this by rebooting the computer with init=/bin/dash on the Linux command line so that it starts nothing besides dash, or (much more conveniently) creating a Linux container using unshare -Urfp /bin/dash in a terminal (unshare doesn't actually create a blank OS, but it does simulate one; notably, the inside of the container thinks it's root, dash thinks it's init, etc., just as would happen on an actual blank OS). Disclaimer: I haven't actually tested this on bare metal yet, only inside unshare, but it should work both ways in theory.

Finally, once we've got that set up, we simply have to look at the PID; as we're on a blank system, the init system (here, the shell) will have PID 1, so the executions of Perl will have PIDs from 2 up to 12 inclusive. So our program looks like this:

say 12-$$||die

Here's a sample run:

$ unshare -Urfp /bin/dash
# perl -E 'say 12-$$||die'
10
# perl -E 'say 12-$$||die'
9
# perl -E 'say 12-$$||die'
8
# perl -E 'say 12-$$||die'
7
# perl -E 'say 12-$$||die'
6
# perl -E 'say 12-$$||die'
5
# perl -E 'say 12-$$||die'
4
# perl -E 'say 12-$$||die'
3
# perl -E 'say 12-$$||die'
2
# perl -E 'say 12-$$||die'
1
# perl -E 'say 12-$$||die'
Died at -e line 1.
#

6502 machine language + Apple ][+ ROM, 12 (11? 10? 9?) bytes

CE 06 80 F0 01 A2 0B A9 00 4C 24 ED

Should start at $8000. Crashes to the system monitor when the count reaches 0.


C6 B6 F0 01 A2 0B A9 00 4C 24 ED

Should start at $B1. This saves a byte since I can use the (two-byte) zero-page version of DEC, but overwrites the critical Applesoft routine CHRGET; you'll need to load it and call it from the monitor, and use CTRL+BReturn to re-initialize BASIC once you're done. Not sure if this invalidates it or not.


CE 06 80 F0 01 A2 0B 4C 26 ED

Should start at $8000. This doesn't initialize $9E, saving two bytes. However, this means you must not call it with a negative address (or, if you call it from the monitor, you have to call the monitor with a positive address). If you do, Applesoft's CALL routine will store FF in $9E, causing it to add 65280 to the number when printing it. Again, not sure if this invalidates the solution or not.


C6 B6 F0 01 A2 0B 4C 26 ED

Should start at $B1. This is a combination of the above two programs, saving a total of three bytes; you'll have to call the monitor with a positive address, load it and run it from there, and use Ctrl+BReturn to re-initialize BASIC once you're done.


Note that these programs only modify the program in memory; re-loading the program from disk will reset the countdown. This works because the Apple ][ (and ][+, //e, and //c) have no memory protection system whatsoever; the program (and its self-modifications) will stay in memory even after it exits, so you can keep running it from memory until you overwrite that memory with something else.


Sample run

]BLOAD COUNT THEN BRK
]CALL 32768
10
]CALL 32768
9
]CALL 32768
8
]CALL 32768
7
]CALL 32768
6
]CALL 32768
5
]CALL 32768
4
]CALL 32768
3
]CALL 32768
2
]CALL 32768
1
]CALL 32768

8008-    A=80 X=9D Y=00 P=36 S=EE
*

Explanation

     DEC NUM+1  ; Decrement the LDX instruction's operand
     BEQ NUM+1  ; If it is now zero, branch to it; 00 is the opcode for the BRK instruction, which causes the program to crash to the monitor
NUM  LDX #$0B   ; Load the X register with 0x0A; the operand has already been decremented once
     LDA #$00   ; Load the accumulator with 0
     JMP $ED24  ; Jump to $ED24, an Applesoft ROM routine which prints A (high byte),X (low byte) in decimal

Explanation of 10 byte version

     DEC NUM+1  ; Decrement the LDX instruction's operand
     BEQ NUM+1  ; If it is now zero, branch to it; 00 is the opcode for the BRK instruction, which causes the program to crash to the monitor
NUM  LDX #$0B   ; Load the X register with 0x0A; the operand has already been decremented once
     JMP $ED26  ; Jump to $ED26, which is two bytes into the Applesoft routine at $ED24. The two skipped bytes would store the accumulator in $9E

Variants

Prints ERR and beeps when count reaches 0

Normal - 15 bytes

CE 06 80 F0 07 A2 0B A9 00 4C 24 ED 4C 2D FF

Overwrites CHRGET - 14 bytes

C6 B6 F0 07 A2 0B A9 00 4C 24 ED 4C 2D FF

Doesn't initialize $9E - 13 bytes

CE 06 80 F0 05 A2 0B 4C 26 ED 4C 2D FF

Overwrites CHRGET and doesn't initialize $9E - 12 bytes

C6 B6 F0 05 A2 0B 4C 26 ED 4C 2D FF

Freezes when count reaches 0

Normal - 12 bytes

CE 06 80 F0 FE A2 0B A9 00 4C 24 ED

Overwrites CHRGET - 11 bytes

C6 B6 F0 FE A2 0B A9 00 4C 24 ED

Doesn't initialize $9E - 10 bytes

CE 06 80 F0 FE A2 0B 4C 26 ED

Overwrites CHRGET and doesn't initialize $9E - 9 bytes

C6 B6 F0 FE A2 0B 4C 26 ED

Bash + sed, 41 40 38 bytes

echo $[n=10/1]
sed -i s/$n/$[n-1]/g $0

Try it online!