Hacker News new | comments | show | ask | jobs | submit login
Paint16b: A 16 byte paint program written in 12 lines (sizecoding.org)
192 points by rishabhd 22 days ago | hide | past | web | favorite | 68 comments



And if you liked that http://www.hugi.scene.org/compo/compoold.htm

And something I found useful https://www.muppetlabs.com/~breadbox/software/tiny/teensy.ht...

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.


Thanks ! Is there anything about Tetris like game ?

I found this : https://developers.slashdot.org/story/12/02/19/1351213/tetri... but it's in Javascript.

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


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_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!


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


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


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.


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.


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


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


Not unique to Wintel, or this program. That's the reason programs were so small for the original Macintosh. Most of the essential routines, especially Quicktime, were in ROM.


(I think you mean QuickDraw.) Yes, and the ROM itself was only 64kb of hand-written assembly, using a variety of techniques to cram in all that functionality, like sharing function prologues and epilogues.


Yep. QuickDraw. Been too long.


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.


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


Apparently these are a valid assumption to make in DOS.


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 =]


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



> assume ah=0; assume bx=0

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


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

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


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.


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


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


Saves one byte, I assume. Register targets are typically encoded in the opcode while direct values follow the opcode byte.


Probably because (IIRC) mov is 2 bytes and inc is only 1 byte and they're optimizing for minimum size.


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


Is assembly not common knowledge anymore?


It's interesting how many people would not recognize assembly language if shown a sample. In a way that is a statement as to how far compilers have come, that it is possible to produce perfectly good software that runs fast enough that only a limited number of people still has to know about the details of the underlying processor.

And in the case of interpreted languages or JIT'd languages the lines get blurred a bit more but even there it is clear that whoever uses say 'numpy' does not need an understanding of the underlying operations to get through their working day.

Higher levels of abstraction translate into higher productivity, in ever more rare cases it is worth breaking open those black boxes to go to work on the underlying plumbing.


Now I'm wondering if there are frontend devs who don't know where the name "WebAssembly" comes from...


If IT person doesn't know even the general looks of assembly code then something is wrong with the education. For non-IT that's another topic


lots of 'programmers' don't even know what a debugger or a cpu is xD let's not talk about common knowledge in 2018 please its depressing :D


Last week I had to explain to an IT guy what a tractor feed printer is. He'd never heard of "tractor feed."


ASM is arguably not obsolete technology, while tractor feed pretty clearly is.


I'm not even sure if C is.


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.


I admit my experience is limited, but there are few things that should immediately identify some code as assembly to someone with previous experience, regardless of the platform, such as the prevalence of the "mov" command, a "jump" command or the use of registers, using memory addresses as parameters, accssing stack using "push" and "pop" commands.

Registers might have different names depending on the platform, actual command names may vary, but I would be surprised to see non-obscure platforms for which the pattern above is not valid.


I suppose my gp wasn't very clear. Language in this context could be just ASM or it could be intel x86 DOS ASM. As I said, the gp could be a master of 6502 ASM, but not be able to tell the difference between ARM and X86.


The question is whether it's recognizable as assembly at all rather than which specific flavor. I think it's correct that all assembly flavors tend to look very similar to each & very dissimilar from any language.


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


I'm the worst type of bootcamp web developer you can imagine.

I only knew it was Assembly because i once looked at the apollo guidance computer's source code.


Not if it is your first time seeing assembly.


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


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


The syntax is different but the structure (or... lack thereof) is almost always the same (excluding the "edge cases" like highly-macroised code.) Asm looks like a step-by-step list of instructions, which is exactly what it is.

Confusing Asm with early (GOTO-only) BASIC, on the other hand, would seem far more common to me.



This applies to general public but someone with Freelance Web developer based in Amsterdam. Mostly Node and React. in their bio should probably know what Assembly is...


Should they? Why?

Look, I wrote assembly when I was a teen, went to school for CS twice and can't do squat with it today. I know it is ASM but beyond that it's not that interesting or exciting to me.

This person's job is strictly to build frontend web apps. Knowledge of assembly is largely ornamental to that pursuit. I'm not even sure someone getting as BSCS and MSCS would spend enough time with it to care.

It's important, critically so for some people, but I wouldn't ding a job applicant for a node job for not recognizing it.


I don’t think everyone who writes code needs to know how to write assembly, but I think it’s important to know that it exists and possibly what it looks like. Maybe not while you’re still learning to code, but once you’ve actually started working in the industry it’s a good thing to know, if only because it gives you a bit of context of how computers actually work.


1) No, you only need to know about assembly language if you go for deep microoptimizations.

2) Assembly is vastly more high level than modern hardware, so much that most of those CISC instructors are broken into tens to thousands micro operations. It's not even close to any kind of machine architecture course. You will need that kind of deep knowledge to write optimal compilers which probably take (extended) C and not assembly language.


Assembly (well, machine code if you want to nitpick) is still the representation of a program right at the interface between software and hardware. It's the processor's native API, no matter how many layers of additional abstraction behind that API. So it's a pretty important level of abstraction if you want to have a reasonable high-level idea of how a computer works.


Why? Serious question. Nothing about that bio says “this person should know about the existence of assembly” to me.

And besides, with their question they’ve made an effort to learn about it so I don’t see any reason to criticize them for not knowing. It’s like the xkcd referenced says, every day there are a bunch of people hearing about something for the first time. We should be happy that they hear about it!


Really? A programmer of any stripe who doesn't recognize assembly just seems like a very wrong thing. One wonders what their mental model of the computer is like.


I imagine that most people abstract at the levels above and below assembly languages. E.g. they know about high level languages, and that a CPU cannot directly run that, but their view of the lower level probably does not include any human readable artifacts such as latin characters but rather binary and voltages.


We have web pages with multiple seconds of latency because devs don't have a deep understanding of the performance hits of composing programs. And for that you need to understand how the CPU works.


I do not believe that knowing ASM would even remotely translate into the ability to optimize web pages for latency: understanding high-level concepts like TLS, HTTP/HTTP2, and being to understand JS engines seems far more applicable.

You also seem to be making the assertion that you cannot understand how a CPU works without knowing ASM, which seems equally spurious.


ASM is fairly useless knowledge for that problem, unless you go in the deep weeds of internals of your JS interpreter.


There is a significant portion of developers ( particular web and app ) and IT staff who never studied computer science in college but instead moved into programming/IT mid career. Most of them probably never heard of or seen assembly code.

However, I would be shocked if anyone who went through a CS program wasn't aware of assembly programming.


CS tends to be algorithms not low level programming. Unlike say robotics or electronics.


Even so, don't most CS departments have an assembly class? Or maybe get introduced to it in a Computer Architecture class?


CS usually includes classes on compilers, at least that's where I learnt some assembly


16 bit x86 assembly. It will run on DOS with a VESA framebuffer and mouse driver.

Dosbox with it's default config should work fine.


Nitpicking, but this is not a VESA mode, it is plain VGA mode 12h (640x480x16).


It's obvious in the context of that wiki. From the front page:

> SizeCoding.org is a wiki dedicated to the art of creating very tiny programs for the 80x86 family of CPUs. By "very tiny programs", we mean programs that are 256 bytes or less in size, typically created by members of the demoscene as a show of programming skill. The size of these tiny programs is measured by their total size in opcode bytes, and are usually presented as executable .COM files to be run in pure DOS, a DOS VM running inside another operating system, or an emulator that can run DOS such as DOSBox.


x86 16-bit assembly using MS-DOS system calls.


can I run this on linux? (wine?)


I guess you can in Dosbox? https://www.dosbox.com/


You cannot on linux (protected mode) , you may in Wine (see the FAQ about DOS and mmap problems) and you can in DOSBox.


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

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




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: