
Paint16b: A 16 byte paint program written in 12 lines - rishabhd
http://www.sizecoding.org/wiki/Paint16b
======
benj111
And if you liked that
[http://www.hugi.scene.org/compo/compoold.htm](http://www.hugi.scene.org/compo/compoold.htm)

And something I found useful
[https://www.muppetlabs.com/~breadbox/software/tiny/teensy.ht...](https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html)

It really is quite amazing how small you can make things when you try. Then
you realise you're wasting most of a disc sector, and the thing you have is
unportable and unmaintainable. But still, being able to look at a binary and
know what every byte does, being unconstrained by the rules of a language is
liberating.

~~~
dalf
Thanks ! Is there anything about Tetris like game ?

I found this :
[https://developers.slashdot.org/story/12/02/19/1351213/tetri...](https://developers.slashdot.org/story/12/02/19/1351213/tetris-
in-140-bytes) but it's in Javascript.

A long time ago I wrote this :
[https://github.com/dalf/stetris/blob/master/STETRIS-
UTF8.ASM](https://github.com/dalf/stetris/blob/master/STETRIS-UTF8.ASM)

------
userbinator
All the program has to do is repeatedly get the current mouse position and set
the pixel there, and that's all it does. These two links should help make
sense of it:

[http://stanislavs.org/helppc/int_33.html](http://stanislavs.org/helppc/int_33.html)

[http://stanislavs.org/helppc/int_10.html](http://stanislavs.org/helppc/int_10.html)

The main loop contains an implicit state-machine that does multiple things on
different iterations, which is partly responsible for the small size.

Another thing that you can clearly see is how efficient it is at using the
registers; this is one area where compiler-generated code is consistently
behind, and something I wish compilers would get better at --- an HLL could
take as many bytes as this entire program just to put the parameters onto the
stack and call a function.

Finally, one small correction:

    
    
        ret             ; assume [[FFEE]] = [0] = CD20 = int 20
    

...should be "assume [ _FFFE_ ] = 0", because that's where the stack pointer
is initially, and this program does not use the stack itself for its entirety
--- it doesn't need to!

~~~
saagarjha
Why is the ret necessary? Is this not an infinite loop?

~~~
userbinator
The loop instruction decrements CX, and then jumps if it is nonzero. Otherwise
it "falls through". At that point CX contains the X coordinate of the cursor.
Hence the comment:

    
    
        ; allows exit if the mouse is leftmost

~~~
saagarjha
Ah, I had zoomed in to see the code and completely disregarded the comments;
in hindsight, I see that was a mistake. Although, I guess I should have
guessed that cx could contain 0 in it.

------
basementcat
Not to poo-poo this (I think this is very impressive) but the reason why it is
so small is that it just calls a bunch of bios and mouse driver routines. If
those items didn’t already exist the program would surely be larger.

~~~
EForEndeavour
Exactly. Every language has its impressive one-liners and terse programs
thanks to the underlying layers of abstraction, even asm.

~~~
prab97
"even ASM" \- I read that twice to feel good.

------
voltagex_
[https://www.pouet.net/prod.php?which=63826](https://www.pouet.net/prod.php?which=63826)
has download links. In theory if someone were to put this onto archive.org you
could use the JS version of DosBox to try it out.

------
vectorEQ
pretty cool, but why not have it a tiny tiny bit longer code and not strt with
2 flat out assumptions? I mean im not familiar with dos deeply and if these
will be true 100% of the time, but as it reads, it reads like silly code which
might crash or work unexpectedly in certain situations.

either way cool! :D

~~~
saagarjha
Apparently these are a valid assumption to make in DOS.

~~~
vectorEQ
cool :) learning more about low level codes, and everyone tells me never to
assume anything about a booting system ,but i suppose DOS will offer a more
stable environment for programs to start then 'any x86_^64 hardware' :D thanks
for clearing that up! it's a simple program but it sure shows that you can
already do a lot with just a few bytes =]

------
AstralStorm
Sure, it's 12 lines of function calls to advanced DOS APIs and no actually
useful functionality.

------
jeffrallen
Right, but can it read email?
[https://en.wikipedia.org/wiki/Jamie_Zawinski#Principles](https://en.wikipedia.org/wiki/Jamie_Zawinski#Principles)

------
stuartmalcolm
> assume ah=0; assume bx=0

Hmm. So the _second_ time this program is run, it will fail?

~~~
chris_overseas
No, AFAIK MS-DOS always initialises it to zero before starting the program.
I'm trying to find a better reference, but I think this[0] effectively
explains what state the registers are in on entry to your program.

Edit: I take that back. According to [1]:

    
    
      .COM-format executables begin running with the following register values:
      AL = 00h if first FCB has valid drive letter, FFh if not
      AH = 00h if second FCB has valid drive letter, FFh if not
    
    

[0]
[https://thestarman.pcministry.com/asm/debug/debug.htm#INIT](https://thestarman.pcministry.com/asm/debug/debug.htm#INIT)

[1]
[http://www.delorie.com/djgpp/doc/rbinter/id/51/29.html](http://www.delorie.com/djgpp/doc/rbinter/id/51/29.html)

~~~
boomlinde
The value of BX is however strictly undefined, but practically always 0.
Potentially some DOS will load this register with a different value, but
probably no version of MS-DOS.

~~~
freefal
But why not explicitly set the register to 1 rather than assume its 0 and
increment it by 1?

~~~
amenghra
On x86, some instructions are longer than others. Incrementing is a single
byte. Setting the value 1 if you don’t assume anything is going to be 2 bytes
(al, ah, bl, etc.) or 4 bytes for (ax, bx, etc.)

This online x86/x64 assembler is great:
[https://defuse.ca/online-x86-assembler.htm](https://defuse.ca/online-x86-assembler.htm)

------
jorisw
Be helpful if the page mentioned what language the program was written in.

~~~
klohto
Is assembly not common knowledge anymore?

~~~
benj111
In the parents defense 'assembly' isn't one thing. Every assembler has a
different syntax, as does every chip family. The parent might be a master of
6502 ASM. And as that XKCD comic says. Everyone has to learn even the most
obvious thing for the first time at some point.

~~~
m0skit0
Hm... If you know one assembly you can easily recognize when it is assembly,
even if you don't know the specific architecture.

~~~
vanderZwan
Not if it is your first time seeing assembly.

~~~
dpcx
Doesn't that then imply that you don't know assembly?

~~~
vanderZwan
Blargh, I somehow read that as _" if you don't know assembly"_ instead of _"
if you know one assembly"_.

------
singularity2001
can I run this on linux? (wine?)

~~~
cpach
I guess you can in Dosbox? [https://www.dosbox.com/](https://www.dosbox.com/)

------
app4soft
_SHPaint_ \-- ANSI Art editor programmed in bash, written in 179 lines.[0,1]

    
    
      #!/bin/bash
      #
      #  Author: Martin "BruXy" Bruchanov, bruxy at regnet.cz
      #
      
      # Check input first
      if [ ! $# -eq 1 ] || [ "$1" == "-h"] || [ "$1" == "--help" ] ; then
      	printf "Usage:\n"	
      	printf "\t$0 saved_image\n"
      	printf "\n\tImage data will be saved into given filename, if file exist\n"
      	printf "\tit will be displayed and ready to edit!\n"
          printf "\tHit <Ctrl-C> any time to quit the program.\n"
      	exit 1
      fi
      
      ##################
      # Initialization #
      ##################
      
      IMAGE_FILE=$1
      _STTY=$(stty -g)    # Save current terminal setup
      printf   "\e[2J"    # clear screen, set cursos at beginning
      stty -echo -icanon  # Turn off line buffering
      printf "\e[?9h"     # Enable terminal mouse reading
      printf "\e[?25l"    # Turn of cursor 
      printf "\e]0;-=[ ShPaint ]=-\007"
      
      # Hash array with image data,
      # ... key is "$Y;$X",
      # ... value ANSI colors and brush "b;F;Bm"
      declare -A IMAGE  
      
      # Defaults
      BRUSHES=(       )
      FG=( {30..37} )
      BG=( {40..47} ) # 49 ... default background
      X=0 
      Y=0
      ERASE=0
      BRUSH=${BRUSHES[3]}
      FG_COLOR="1;${FG[7]}"
      BG_COLOR=49
      
      #############
      # Functions #
      #############
      
      function save_image() {
      	printf "\e[2J" > $IMAGE_FILE
      	for i in ${!IMAGE[@]}
      	do
      		printf "\e[${i}f\e[${IMAGE[$i]}\e[0m"
      	done >> $IMAGE_FILE
      	# set cursor under the image
      	printf "\e[$(tput lines);1f" >> $IMAGE_FILE
      }
      
      function at_exit() {
      	printf "\e[?9l"          # Turn off mouse reading
      	printf "\e[?12l\e[?25h"  # Turn on cursor
      	stty "$_STTY"            # reinitialize terminal settings
      	clear
      	echo "Thank for using ansipaint!"
      	if [ ! -z "$IMAGE_FILE" ] ; then 
      		echo "Your image is saved as '$IMAGE_FILE'."
      		save_image
      	fi
      	exit
      }
      
      # X = $1, Y = $2
      function set_pos() {
       	echo -en  "\e[$2;$1f"
      }
      
      function show_pos() {
      	set_pos 65 1
      	printf "x,y = %3d,%3d" $X $Y
      }
      
      function show_brush() {
      	set_pos 70 2 
      	printf "[ \e[${FG_COLOR};${BG_COLOR}m$BRUSH\e[0m ]"
      }
      
      function process_click() {
      #	X=$1 Y=$2
      	# set foreground color
      	if [ $Y -eq 1 ] || [ $Y -eq 2 ] ; then
      		if [ $X -gt 2 ] && [ $X -lt 28 ] ; then
      			FG_COLOR="$[Y-1];${FG[$[(X-4)/3]]}"
      			ERASE=0
      		fi
      	fi
      	
      	# set background color
      	if [ $Y -eq 1 ] && [ $X -gt 34 ] ; then
      		if [ $X -gt 34 ] && [ $X -lt 59 ] ; then
      			BG_COLOR="${BG[$[X-35]/3]}"
      			ERASE=0
      		else
      			BG_COLOR="49"
      			ERASE=0
      		fi
      	fi	
      
      	# set brush
      	if [ $Y -eq 2 ] && [ $X -gt 36 ] && [ $X -le 51 ] ; then
      		BRUSH=${BRUSHES[$[(X-37)/2]]}
      		ERASE=0
      	fi
      
      	# set erase
      	if [ $Y -eq 2 ] && [ $X -ge 54 ] && [ $X -le 62 ] ; then
      		BRUSH=" "
      		BG_COLOR="49"
      		ERASE=1
      	fi
      
      	# DEBUG
      	# set_pos 0 25
      	# printf "$FG_COLOR $BG_COLOR $BRUSH"
      }
      
      function draw_menu() {
      	set_pos 1 1; echo "FG: "
      	for i in ${FG[*]}
      	do
      		set_pos $[(i-30)*3+4] 1
      		echo -en "\e[${i}m\e[0m"	
      		set_pos $[(i-30)*3+4] 2
      		echo -en "\e[1;${i}m\e[0m"	
      	done
      
      	set_pos 30 1; echo "BG: "
      	for i in ${BG[*]}
      	do 
      		set_pos $[(i-40)*3+35] 1
      		echo -en "\e[${i}m   \e[0m"	
      	done
      	echo "   |" # default background (49)
      
      	set_pos 30 2; echo -en "Brush: ${BRUSHES[*]}"
      	printf "  [ Erase ]"
      	show_brush
      }
      
      function load_image() {
      	if [ -f $IMAGE_FILE ] ; then 
      		data=$(sed -e 's/\x1b/E/g;s/E\[0m/\n/g;s/E\[2J//' $IMAGE_FILE | \
      			sed -n -e 's/E\[\(.*\)fE\[\(.*m.\)/IMAGE["\1"]="\2"/ p')
      		eval $data
      		cat < $IMAGE_FILE
      	fi
      }
      
      ##########
      #  MAIN  #
      ##########
      trap at_exit ERR EXIT 
      load_image
      draw_menu
      while :
      do 
      	read -N 6 click
      	mouse=( `echo -en ${click#???} | hexdump -v  -e'1/1 " %u"'` )
      	X=$[ ${mouse[0]} - 32]	Y=$[ ${mouse[1]} - 32]
      	process_click
      	show_pos 
      	show_brush
      	if [ $Y -gt 2 ] ; then	
      		echo -en  "\e[${Y};${X}f\e[${FG_COLOR};${BG_COLOR}m$BRUSH\e[0m"
      		if [ $ERASE -eq 0 ] ; then
      			IMAGE["${Y};${X}"]="${FG_COLOR};${BG_COLOR}m$BRUSH"
      		else
      			unset IMAGE["${Y};${X}"]
      		fi
      	fi
      done
      
    

[0] [http://bruxy.regnet.cz/web/linux/EN/ansi-art-sh-
paint](http://bruxy.regnet.cz/web/linux/EN/ansi-art-sh-paint)

[1]
[https://www.youtube.com/watch?v=aoAMxMukgPY](https://www.youtube.com/watch?v=aoAMxMukgPY)

