Jump to content

Efecto Matrix en IBM 5150 / 5160 con Monitor 5151 Verde (DigiRain)


Recommended Posts

Todos conocemos el efecto de inicio de la Película Matrix, ha sido reproducido en cantidad de Wallpapers y screensavers pero en este caso en particular, reproducido en una PC IBM XT gracias a un efecto de Ghosting o arrastre del cursor, una particularidad que tienen los monitores originales IBM 5150. Lo bueno, incluye el código en ASM para poder compilarlo y reproducirlo, si tienes el Hardware indicado



GitHUB: https://github.com/OliWright/digirain/blob/main/digirain.asm





  • Upvote 1
Link to comment
Share on other sites

Codigo en Assembler para modificar a gusto:


; That 'digital rain' effect from the Matrix.  It's designed to look
; like it's on an old-school green screen monitor, with the trailing
; letters having an afterglow from a high persistence phosphor.
; But most modern implementations simulate the afterglow.  Have you
; ever wondered how it would look on an _actual_ green screen monitor
; like an IBM 5151?
; Well wonder no more!  Here's a short program that assembles into a
; small .com file that will run on an old PC.  Ideally something like
; a 5150 with a Monochrome Display Adapter connected to a 5151 monitor.
; This should 'run' on CGA too, but the trailing effect relies on a high
; persistence phosphor to work properly.

; To assemble using nasm:
;     nasm -f bin -o digirain.com digirain.asm
; I provide no warranty etc. for this code.  Feel free to use it
; as you see fit, but please credit me if you do.
;   - Oli Wright 2021
; oli.wright.github@gmail.com

; We want to make sure we can run on an IBM PC 5150, so....
[CPU 8086]

NUM_SPRITES               EQU 128
SLOWEST_SPEED             EQU 0x0800 ; Speeds in 1:15 fixed-point format
FASTEST_SPEED             EQU 0x2000 ; characters per frame

; Structure of sprite data
OFFS_ADDRESS              EQU 4

section .text
	; .com files always start 256 bytes into the segment
	org  0x100
	jmp  start

	; Yo!
	db   '>- oli.wright.github@gmail.com -<'

	mov  ax,0x0002        ; 00: Set video mode, 02: 80 column text
	int  0x10

	; Read location 0040:0063 to test if we're on CGA or MDA
	mov  ax,0x0040
	mov  es,ax
	mov  ax,[es:0x0063]
	cmp  ax,0x3b4
	mov  bx,0xb000        ; Segment for MDA text mode
	je   skip_cga
	mov  bh,0xb8          ; Segment for CGA text mode
	mov  es,bx            ; Video segment into ES

	; Disable the cursor
	mov  ah,0x01
	mov  ch,0x3f
	int  0x10

	; Initialise random number seed from the timer
	xor  ax,ax
	mov  di,ax
	int  0x1a             ; Int 1ah/ah=0 get timer ticks since midnight in CX:DX
	mov  [random_seed],dx ; Use lower 16 bits (in DX) for random value

	; Initialise all the sprite data
	mov  cx,NUM_SPRITES
	mov  di,sprites
	mov  bl,100
	xor  ax,ax
	; We deliberately leave some fields uninitialised to save code space.
	; Arrange things so that they'll be initialised during the first iteration
	;mov  [di + OFFS_HORIZONTAL_POS],ax   ; Leave uninitialised
	mov  [di + OFFS_VERTICAL_POS + 1],bl  ; Off screen
	mov  [di + OFFS_ADDRESS],ax
	mov  [di + OFFS_CHARACTER],cl
	loop init_sprite_loop

	; Check for ESC pressed
	mov  ah,0x0b
	int  0x21
	cmp  al,0
	je   start_frame      ; No STDIN waiting
	mov  ah,0x08
	int  0x21
	cmp  al,27
	jne  start_frame      ; Not ESC

	; Shutdown
	mov  ax,0x0002        ; 00: Set video mode, 02: 80 column text
	int  0x10
	mov  ah,0x4c
	int  0x21             ; Terminate

	xor  ax,ax
	mov  si,sprites
	mov  cx,NUM_SPRITES

	; Erase the old sprite
	mov  di,[si + OFFS_ADDRESS]
	mov  al,' '
	mov  [es:di],al

	; Move sprite downward
	mov  ax,dx            ; Speed in 1:15 format
	push cx               ; Save cx sprite counter
	mov  cl,5
	shr  ax,cl            ; Convert speed to 6:10 format
	or   al,1             ; Make sure the lsbs constantly change
	add  ax,[si + OFFS_VERTICAL_POS]
	cmp  ah,100           ; Off the bottom of the screen?
	jl   skip_reset_sprite

	; Reset sprite
	push dx
	mov  ax,25173           ; LCG Multiplier
	mul  word [random_seed] ; dx:ax = LCG multiplier * seed
	add  ax,13849           ; Add LCG increment value
	; Modulo 65536, ax = (multiplier*seed+increment) mod 65536
	mov  [random_seed],ax   ; Update seed
	xor  dx,dx
	mov  cx,80
	div  cx               ; I should be able to use an 8-bit div here, but it crashes DosBox
	mov  ax,dx            ; Remainder of divide by 80
	shl  ax,1
	pop  dx
	mov  [si + OFFS_HORIZONTAL_POS],ax
	xor  ax,ax
	mov  bh,[si + OFFS_VERTICAL_POS + 1] ; Load previous msbs of vertical pos
	mov  [si + OFFS_VERTICAL_POS],ax ; Store the new vertical position
	cmp  ah,bh
	je   skip_change_character

	; Change to a new random character
	mov  bh,al            ; Start with the LSBs of the vertical pos
	xor  bh,dl            ; Mix in the LSBs of the vertical speed
	and  bh,0x7f          ; Restrict to a more pleasing
	add  bh,0x21          ; character range
	mov  [si + OFFS_CHARACTER],bh

	; Calculate start of row
	mov  cl,10
	shr  ax,cl            ; Get integer part of vertical position
	mov  bl,160
	mul  bl
	; Add the horizontal position
	add  ax,[si + OFFS_HORIZONTAL_POS]
	; Save the final address of the sprite
	mov  di,ax
	mov  [si + OFFS_ADDRESS],ax

	; Write the character and attribute to video memory
	mov  al,[si + OFFS_CHARACTER]
	pop  cx;              ; Restore cx as sprite counter
	mov  ah,cl            ; Use the sprite counter to choose attributes
	and  ah,0x08          ; - Bright for half of them
	or   ah,0x07          ; - All are 'white'
	mov  [es:di],ax

	; Next sprite
	loop sprite_loop

	jmp  frame_loop

section .bss

random_seed: resw 1
sprites:     resb NUM_SPRITES * BYTES_PER_SPRITE


  • Upvote 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Create New...