x86 16 bit machine code on MS-DOS - 21 bytes
00000000 53 1f a1 6c 04 05 8e 03 3b 06 6c 04 77 fa b2 6e |S..l....;.l.w..n|
00000010 b4 02 cd 21 c3 |...!.|
00000015
Commented assembly:
org 100h
section .text
start:
push bx ; notice: bx starts as 0
pop ds ; set the data segment to 0; this allows us to
; read the tick count without segment selectors
mov ax,word [046ch] ; read the low 16 bit of the tick count
add ax,910 ; 910 ticks = 49.98 seconds
; here we used ax because the encoding for both
; the mov and the add is one byte shorter
lop:
cmp ax,word [046ch] ; compare the stop time with the current time
ja lop ; loop if still above
mov dl,'n' ; n is for noodles
mov ah,2
int 21h ; print
ret ; quit
This could be shaved down to 16 bytes by replacing the final three instructions with int 18h
, which on ancient machines would invoke the ROM built-in BASIC interpreter and almost everywhere else prints something like "No ROM BASIC" and reboot after a key press, but after discussing this with OP it was decided that this wouldn't be allowed.
Interrupt + self-modifying code approach - 23 bytes
This is another approach; it turns out to be bigger, but I post it anyway because it is way more fun.
00000000 b8 1c 25 ba 12 01 cd 21 b9 8e 03 e2 fb b4 02 cd |..%....!........|
00000010 21 c3 ff 0e 09 01 cf |!......|
00000017
org 100h
section .text
%define counter lop+1 ; see below
start:
; setup interrupt handler
mov ax,251ch ; function 25h (replace interrupt vector)
; interrupt 1ch (user timer)
mov dx,interrupt_handler ; timer ISR
int 21h
lop:
mov cx,910 ; the 910 immediate value is actually pointed by counter,
; which is decremented in interrupt_handler
loop lop ; decrement and loop as long as cx is nonzero (the decrement is
; not relevant, we are always resetting cx at each iteration)
mov ah,2 ; function 2 (print character); dl is already a printable character
int 21h
ret ; quit
interrupt_handler:
dec word[counter] ; at every tick decrement the counter
iret