Image noise: Difference between revisions

m (→‎{{header|6502 Assembly}}: fixed a typo and added clarification)
Line 3,186:
SetVid(3); \restore normal text mode
]</lang>
 
=={{header|Z80 Assembly}}==
{{works with|Game Boy Color}}
Omitting the initialization code and the other logic keeping the machine working, this will focus solely on the code responsible for creating the effect. The "random" noise was generated by loading this program's ROM image in YY-CHR (a binary file editor) and copying it to its own file, then loading that file into the Game Boy Color's video memory. The framerate is usually 60 FPS but occasionally dips to 59. At first, every tile on screen is the same, but the "noise" effect is caused by:
 
* A "frame timer" variable that increments every vertical blank is used as a pseudo-RNG.
* A variable called "cosine_index" is incremented and its cosine is taken, then the screen is scrolled by that amount.
* Each iteration of the main game loop alters the interrupt handler (which is executed in RAM, having been copied from ROM before the main game loop begins) so that the sine is taken rather than the cosine.
* During the main game loop, the frame timer is also used to select a random graphics tile from among 16 possibilities, and draws it to a randomly selected tile on screen using a simple "xor-shift" routine.
 
<lang z80>forever:
ld hl,frametimer
ld a,(hl)
rrca
rrca
rrca
xor (hl) ;crude xorshift rng used to select a "random" location to draw to.
 
ld h,0
ld L,a ;this random value becomes the low byte of HL
 
rlc L
rl h ;reduce the RNG's bias towards the top of the screen by shifting one bit into H.
ld a,&98 ;high byte of gb vram
add h ;h will equal either 0 or 1.
ld H,a ;now H will either equal &98 or &99
ld a,(frametimer) ;it's possible that this value is different from before since every vblank it increments.
;the vblank code was omitted because it's mostly irrelevant to the task.
and &0f ;select one of sixteen tiles to draw to the screen at the randomly chosen VRAM location.
call LCDWait ;disable interrupts, and wait until the Game Boy is ready to update the tilemap
ei ;enable interrupts again.
ld (hl),a ;now we can store the frame timer AND &0F into video ram.
ld hl,vblankflag ;set to 1 by the vblank routine.
xor a
wait:
halt ;waits for an interrupt.
cp (hl) ;
jr z,wait ;vblank flag is still 1 so keep waiting.
ld (hl),a ;clear vblank flag.
 
 
ld hl,&FFFF ;master interrupt register
res 1,(hl) ;disable hblank interrupt.
 
ld bc,2
ld a,(frametimer)
bit 4,a ;check bit 4 of frametimer, if zero, we'll do sine instead of cosine. Bit 4 was chosen arbitrarily.
jr nz,doSine
ld hl,cos
jr nowStore
doSine:
ld hl,sin
nowStore: ;store desired routine as the CALL address
ld a,L
LD (&A000+(callpoint-TV_Static_FX+1)),a
;using label arithmetic we can work out the place to modify the code.
;hblank interrupt immediately jumps to cartridge RAM at &A000
ld a,H
LD (&A000+(callpoint-TV_Static_FX+2)),a ;store the high byte.
 
ld hl,&FFFF
set 1,(hl) ;re-enable hblank interrupt
 
jp forever</lang>
 
And the interrupt handler located at memory address <code>&A000</code>:
<lang z80>TV_Static_FX:
push hl
push af
ld hl,(COSINE_INDEX
inc (hl)
ld a,(hl)
 
callpoint:
call cos ;<--- SMC, gets changed to "CALL SIN" by main game loop when bit 4 of frametimer equals 0, and back to
;"CALL COS" when bit 4 of frametimer equals 1.
; returns cos(A) into A
 
LDH (&43),A ;output A to horizontal scroll register.
done_TV_Static_FX:
pop af
pop hl
reti ;return to main program
TV_Static_FX_End:</lang>
 
{{out}}
[https://ibb.co/Ns7jy0x Output image of program]
1,489

edits