Discussion:
Space Invaders
(too old to reply)
Mike Gonta
2017-02-09 23:59:43 UTC
Permalink
SPACE INVADERS by PAUL S. REID Version 1.1
An Exact Clone of the Arcade Game in 100% Assembler
Game, Source Code, and Documentation Included
Freely Distributable - No Shareware Fee
Released September 17, 1995 - MINOR BUG FIXES

Ever so slightly updated version to assemble with FASM / NASM
Bootable and completely stand alone 8086 compatible assembly
Only requires a classic BIOS or UEFI enabled CSM
1.44MB floppy disk image
Will boot and run from an actual floppy disk
Run it in an emulator
Transfer it to a USB flash drive for boot and run fun!
See LICENSE.md and Space Invaders Document.txt

http://mikegonta.com/Space_Invaders


Mike Gonta
look and see - many look but few see

http://mikegonta.com
r***@nospicedham.gmail.com
2017-02-14 00:52:35 UTC
Permalink
Hi,
Post by Mike Gonta
SPACE INVADERS by PAUL S. REID Version 1.1
Ever so slightly updated version to assemble with FASM / NASM
Just to be complete, here's my NASM script to reassemble the
vanilla (unmodified) DOS version (tested with either GNU sed or
Laurent Vogel's Cheap sed):

*****************************************************************
@echo off
if "%SED%"=="" set SED=sed
if "%NASM%"=="" set NASM=nasm
if not exist invaders.asm goto end
echo %%SED%% = '%SED%'
echo %%NASM%% = '%NASM%'
:fix1
echo /^ *\([^ ]\+\) \+[dD]\([bBwWdD]\) .\+/!d>fix1.sed
echo "s||\1\\>/\2[\&]/|"| %SED% -e "s/^.//" -e "s/.$//">>fix1.sed
echo s/B\[/byte ptr [/>>fix1.sed
echo s/W\[/word ptr [/>>fix1.sed
echo s/D\[/dword ptr [/>>fix1.sed
echo "s|^|s/\\<|"| %SED% -e "s/^.//" -e "s/.$//">>fix1.sed
if not exist fix1.sed goto end
:fix2
echo 1i\>fix2.sed
echo %idefine offset\>>fix2.sed
echo %idefine ptr>>fix2.sed
echo /^ *;/b>>fix2.sed
echo s/LEA \+\([^ ]\+\), *\([^ ]\+\)/MOV \1,OFFSET \2/>>fix2.sed
echo /CODE_SEG/d>>fix2.sed
echo /^ *END/d>>fix2.sed
echo s/\]\[\([1-9]\)\]/+\1]/>>fix2.sed
echo s/\[0\]//>>fix2.sed
echo s/\]\(+BX\)/\1/>>fix2.sed
echo s/\[\([^ ]\+ ptr\)/\1/>>fix2.sed
echo s/\(ES:\)\(\[\)/\2\1/>>fix2.sed
echo s/ \+PROC/: ;&/>>fix2.sed
echo / ENDP/s/^/;/>>fix2.sed
echo s/\(Word Ptr\) dword ptr/\1/>>fix2.sed
if not exist fix2.sed goto cleanup
:fix3
echo /RemoveNewInt9:/,/ RET/s/OldInt9Addr/cs:&/>fix3.sed
echo /NewInt9Handler:/,/NotIntercept:/{>>fix3.sed
echo s/byte ptr \[/&cs:/>>fix3.sed
echo /cs:/!s/\(MOV \+\)\([^,]\+\)/\1cs:\2/>>fix3.sed
echo }>>fix3.sed
echo /NotIntercept:/,/^ *$/{>>fix3.sed
echo s/StoreAX/cs:&/>>fix3.sed
echo s/40://>>fix3.sed
echo }>>fix3.sed
if not exist fix3.sed goto cleanup
:asmvars
echo /^ *;/b>asmvars.sed
echo / [dD][bBwWdD] /b>>asmvars.sed
echo /, *OFFSET/b>>asmvars.sed
echo /LEA /b>>asmvars.sed
%SED% -f fix1.sed invaders.asm >>asmvars.sed
if not exist asmvars.sed goto cleanup
%SED% -f asmvars.sed invaders.asm|%SED% -f fix2.sed -f fix3.sed >inv-nasm.asm
if not exist inv-nasm.asm goto cleanup
:build
%NASM% -O9 -o inv-nasm.com inv-nasm.asm
if not exist inv-nasm.com goto cleanup
echo.
dir inv-nasm.com | grep -i "com"
echo.
echo INV-NASM.COM FFF22EF9
crc32 inv-nasm.com
echo.
:cleanup
if "%1"=="notclean" goto end
del fix*.sed >NUL
del asmvars.sed >NUL
:end
if "%SED%"=="sed" set SED=
if "%NASM%"=="nasm" set NASM=
*****************************************************************

And here's a makefile for cross-building atop Linux (e.g. antiX):

*****************************************************************
# GNUmakefile
.RECIPEPREFIX := _

#=== fix1.sed begins ===
# /^ *\([^ ]\+\) \+[dD]\([bBwWdD]\) .\+/!d
# s||\1\\>/\2[\&]/|
# s/B\[/byte ptr [/
# s/W\[/word ptr [/
# s/D\[/dword ptr [/
# s|^|s/\\<|
#=== fix1.sed ends ===

#=== fix2.sed begins ===
# 1i\
# %idefine offset\
# %idefine ptr
# /^ *;/b
# s/LEA \+\([^ ]\+\), *\([^ ]\+\)/MOV \1,OFFSET \2/
# /CODE_SEG/d
# /^ *END/d
# s/\]\[\([1-9]\)\]/+\1]/
# s/\[0\]//
# s/\]\(+BX\)/\1/
# s/\[\([^ ]\+ ptr\)/\1/
# s/\(ES:\)\(\[\)/\2\1/
# s/ \+PROC/: ;&/
# / ENDP/s/^/;/
# s/\(Word Ptr\) dword ptr/\1/
#=== fix2.sed ends ===

#=== fix3.sed begins ===
# /RemoveNewInt9:/,/ RET/s/OldInt9Addr/cs:&/
# /NewInt9Handler:/,/NotIntercept:/{
# s/byte ptr \[/&cs:/
# /cs:/!s/\(MOV \+\)\([^,]\+\)/\1cs:\2/
# }
# /NotIntercept:/,/^ *$/{
# s/StoreAX/cs:&/
# s/40://
# }
#=== fix3.sed ends ===

.PHONY: all clean

unexport UNZIP

SED=sed
NASM=nasm
NASMFLAGS=-fbin -O9v
MD5SUM=md5sum
WGET=wget
WGETOPT=-q
UNZIP=unzip
UNZIPOPT=-qjLan
GAMEURL=ftp.lanet.lv/ftp/mirror/x2ftp/msdos/programming/gamesrc/
GAMEZIP=invadr11.zip

all: inv-nasm.com

inv-nasm.com: inv-nasm.asm
_@$(NASM) $(NASMFLAGS) $< -o $@
_@$(MD5SUM) $@
***@echo 5d6fa26af01606feb90f17e014390139 \ inv-nasm.com

fix1.sed fix2.sed fix3.sed: $(lastword $(MAKEFILE_LIST))
_@$(SED) -e '/$@ begins ===/,/$@ ends ===/!d' $< | $(SED) -e 's/^# *//' >$@

asmvars.sed: fix1.sed invaders.asm
***@echo '/^ *;/b' >$@
***@echo '/ [dD][bBwWdD] /b' >>$@
***@echo '/, *OFFSET/b' >>$@
***@echo '/LEA /b' >>$@
_@$(SED) -f $^ >>$@

inv-nasm.asm: invaders.asm asmvars.sed fix2.sed fix3.sed
_@$(SED) -f asmvars.sed $< | $(SED) -f fix2.sed -f fix3.sed >$@

$(GAMEZIP):
_@$(WGET) $(WGETOPT) $(GAMEURL)$(GAMEZIP)

invaders.asm: $(GAMEZIP)
_@$(UNZIP) $(UNZIPOPT) $< "*/$@"

clean:
_@$(RM) inv-nasm.* fix?.sed asmvars.sed
*****************************************************************
p***@nospam.demon.co.uk
2017-02-15 19:10:36 UTC
Permalink
Post by r***@nospicedham.gmail.com
GAMEURL=ftp.lanet.lv/ftp/mirror/x2ftp/msdos/programming/gamesrc/
GAMEZIP=invadr11.zip
Just a minor update: I checked that URL and it didn't work (for me
anyway). I found the zip at

GAMEURL=ftp.lanet.lv/pub/mirror/x2ftp/msdos/programming/gamesrc/

Probably the site has changed since that script was written?
Pete
--
Believe those who are seeking the truth.
Doubt those who find it. - André Gide
r***@nospicedham.gmail.com
2017-02-16 07:10:31 UTC
Permalink
Hi,
Post by p***@nospam.demon.co.uk
Post by r***@nospicedham.gmail.com
GAMEURL=ftp.lanet.lv/ftp/mirror/x2ftp/msdos/programming/gamesrc/
GAMEZIP=invadr11.zip
Just a minor update: I checked that URL and it didn't work (for me
anyway). I found the zip at
GAMEURL=ftp.lanet.lv/pub/mirror/x2ftp/msdos/programming/gamesrc/
Probably the site has changed since that script was written?
No, it still works for me. But there are alternate URLs:

* ftp://ftp.lyx.org/pub/freedos/files/games/invaders/
* http://www.ibiblio.org/pub/micro/pc-stuff/freedos/files/games/invaders/
r***@nospicedham.gmail.com
2017-07-16 02:00:00 UTC
Permalink
Hi again,

I almost hate to revive this topic, but I did improve my scripts.
It works on Windows now, too (so no messy manual editing needed
to avoid shell escape quirks). I also did minor revisions to the
GNUmakefile to stay in sync.
*****************************************************************
@echo off

::#--- fix1.sed begins ---
:: /^ *\([^ ]\+\) \+D\([BWD]\) .\+/!d
:: s||\1\\>/\2[\&]/|
:: s/B\[/byte[/
:: s/W\[/word[/
:: s/D\[/dword[/
:: s|^|s/\\<|
::#--- fix1.sed ends ---

::#--- fix2.sed begins ---
:: /^ *;/b
:: s/LEA \+\([^ ]\+\), *\([^ ]\+\)/MOV \1,\2/
:: /CODE_SEG/d
:: /^ *END/d
:: s/\]\[\([1-9]\)\]/+\1]/
:: s/\[0\]//
:: s/\]\(+BX\)/\1/
:: s/\[\([^ ]\+ *\[\)/\1/
:: s/\(ES:\)\(\[\)/\2\1/
:: s/ \+PROC/: ;&/
:: / ENDP/s/^/;/
:: s/Word Ptr dword/word/
:: s/\<O[fF][fF][sS][eE][tT]\>//g
:: s/ORG \+100h/[&]/
::#--- fix2.sed ends ---

::#--- fix3.sed begins ---
:: /RemoveNewInt9:/,/ RET/s/OldInt9Addr/cs:&/
:: /NewInt9Handler:/,/NotIntercept:/{
:: s/byte\[/&cs:/
:: /cs:/!s/\(MOV \+\)\([^,]\+\)/\1cs:\2/
:: }
:: /NotIntercept:/,/^ *$/{
:: s/StoreAX/cs:&/
:: s/40://
:: }
::#--- fix3.sed ends ---

::#--- asmvars.sed begins ---
:: /^ *;/b
:: / D[BWD] /b
:: /, *OFFSET/b
:: /LEA /b
::#--- asmvars.sed ends ---

if not exist invaders.asm goto end
if not exist %0 %0.bat %1

if "%SED%"=="" set SED=sed
if "%NASM%"=="" set NASM=nasm

echo.
echo %%SED%% = '%SED%'
echo %%NASM%% = '%NASM%'
if not "%NASMENV%"=="" echo %%NASMENV%% = '%NASMENV%'
REM set NASMENV=-a

set B1=begins ---
set E1=ends ---
set S1=fix1.sed fix2.sed fix3.sed asmvars.sed
for %%a in (%S1%) do %SED% -n -e "/%%a %B1%/,/%%a %E1%/s/^:: *//w %%a" %0
for %%z in (S1 E1 B1) do set %%z=

for %%a in (fix1 fix2 fix3 asmvars) do if not exist %%a.sed goto end

%SED% -f fix1.sed invaders.asm >>asmvars.sed
%SED% -f asmvars.sed invaders.asm|%SED% -f fix2.sed -f fix3.sed >inv-nasm.asm

%NASM% -fbin -O9 inv-nasm.asm -o inv-nasm.com
if not exist inv-nasm.com goto end

echo.
echo INV-NASM.COM FFF22EF9
crc32 inv-nasm.com
echo.

if "%1"=="notclean" goto end
del asmvars.sed >NUL
del fix?.sed >NUL
del inv-nasm.asm >NUL

:end
if "%NASM%"=="nasm" set NASM=
if "%SED%"=="sed" set SED=
*****************************************************************
*****************************************************************
# GNUmakefile
.RECIPEPREFIX := _

#=== fix1.sed begins ===
# /^ *\([^ ]\+\) \+D\([BWD]\) .\+/!d
# s||\1\\>/\2[\&]/|
# s/B\[/byte[/
# s/W\[/word[/
# s/D\[/dword[/
# s|^|s/\\<|
#=== fix1.sed ends ===

#=== fix2.sed begins ===
# /^ *;/b
# s/LEA \+\([^ ]\+\), *\([^ ]\+\)/MOV \1,\2/
# /CODE_SEG/d
# /^ *END/d
# s/\]\[\([1-9]\)\]/+\1]/
# s/\[0\]//
# s/\]\(+BX\)/\1/
# s/\[\([^ ]\+ *\[\)/\1/
# s/\(ES:\)\(\[\)/\2\1/
# s/ \+PROC/: ;&/
# / ENDP/s/^/;/
# s/Word Ptr dword/word/
# s/\<O[fF][fF][sS][eE][tT]\>//g
# s/ORG \+100h/[&]/
#=== fix2.sed ends ===

#=== fix3.sed begins ===
# /RemoveNewInt9:/,/ RET/s/OldInt9Addr/cs:&/
# /NewInt9Handler:/,/NotIntercept:/{
# s/byte\[/&cs:/
# /cs:/!s/\(MOV \+\)\([^,]\+\)/\1cs:\2/
# }
# /NotIntercept:/,/^ *$/{
# s/StoreAX/cs:&/
# s/40://
# }
#=== fix3.sed ends ===

.PHONY: all check clean cleanall

unexport UNZIP

PROG=inv-nasm
OLDASM=invaders.asm

SED=sed
NASM=nasm
NASMFLAGS=-fbin -O9v -a
MD5SUM=md5sum
WGET=wget
WGETOPT=-q
UNZIP=unzip
UNZIPOPT=-qjLan

GAMEZIP=invadr11.zip

#GAMEURL=ftp.lanet.lv/ftp/mirror/x2ftp/msdos/programming/gamesrc/
GAMEURL=www.ibiblio.org/pub/micro/pc-stuff/freedos/files/games/invaders/

all: $(PROG).com check

$(PROG).com: $(PROG).asm
_@$(NASM) $(NASMFLAGS) $< -o $@

fix1.sed fix2.sed fix3.sed: $(lastword $(MAKEFILE_LIST))
_@$(SED) -e '/$@ begins ===/,/$@ ends ===/!d' $< | $(SED) -e 's/^# *//' >$@

asmvars.sed: fix1.sed $(OLDASM)
_@$(SED) -f $^ >$@

$(PROG).asm: $(OLDASM) asmvars.sed fix2.sed fix3.sed
_@$(SED) -e '/^ *;/b' -e '/ D[BWD] /b' -e '/, *OFFSET/b' -e '/LEA /b'\
-f asmvars.sed $< | $(SED) -f fix2.sed -f fix3.sed >$@

$(GAMEZIP):
_@$(WGET) $(WGETOPT) $(GAMEURL)$(GAMEZIP)

$(OLDASM): $(GAMEZIP)
_@$(UNZIP) $(UNZIPOPT) $< "*/$@"

check: $(PROG).com
_@$(MD5SUM) $<
***@echo 5d6fa26af01606feb90f17e014390139 \ $<

clean:
_@$(RM) $(PROG).asm fix?.sed asmvars.sed

cleanall: clean
_@$(RM) $(PROG).com

# EOF
*****************************************************************

It might be faster conversion, but I can't (easily) test that on
old machines. The best way to speed up is minimize the fluff in
the original source, which I call "tidy"ing (optional):

*****************************************************************
# tidy.sed begins
s/\t\+/ /g
s/^\([a-zA-Z0-9_]\+:\) *\([^ ]\+\)/\1\n\2/
s/;[^']*$//
s/;[^']*'[^']*$//
/^ *;/d
s/ \+$//
s/^ *\([^ ]\+\) \+/\1 /
s/^ *\([^ ]\+\) \([^ ]\+\) \+/\1 \2 /
s/^ \+//
/^ *$/d
# tidy.sed ends
*****************************************************************

Have fun!
r***@nospicedham.gmail.com
2019-09-05 03:54:37 UTC
Permalink
Hi again,
Post by r***@nospicedham.gmail.com
I almost hate to revive this topic, but I did improve my scripts.
Much simpler now (and more standard/portable sed):

INV-FASM.SED :
*****************************************************************
1i\
OFFSET equ\
Offset equ\
LEA equ MOV
/^;/b
/ D[BW] /b
/LEA /b
s/\[0\]//
/,O[fF]/b
/CODE_SEG/d
/END/d
s/ DD / DW 0,/
s/Word Ptr //
s/ *PROC .*/:/
s/ES:\[/[ES:/
s/40://
s/\[\([0-9]\)\]/_\1/
s/\([A-Z][a-zA-Z][a-zA-Z][a-zA-Z0-9_]*\),/[\1],/
s/,\([A-Z][a-zA-Z][a-zA-Z][a-zA-Z0-9_]*\)/,[\1]/
s/\([ID].C\) *\([A-Z][^ ][^ ][^ ]*\)/\1 [\2]/
/_[0-9]\]/s/_/+/
/RemoveNewInt9:/,/CLC/s/\[\([^0]\)/[cs:\1/
*****************************************************************

INV-NASM.SED :
*****************************************************************
1i\
%idefine offset\
%define LEA MOV\
%define B byte\
%define W word\
%include "inv-nasm.inc"
s/ DD / DW 0,/
/ D[BW] /{
h
s/^\([A-Z][^ ]*\) *D\(.\) .*/%define s_\1 \2/w inv-nasm.inc
g
b
}
/^;/b
/LEA /b
s/\[0\]//
/,O[fF]/b
/CODE_SEG/d
/END/d
s/ *PROC .*/:/
s/ES:\[/[ES:/
s/40://
s/\[\([1-9]\)\]/+\1/
s/Word Ptr //
s/\[\(.*[+]BX\)\]/\1/
s/\([A-Z][a-zA-Z][a-zA-Z][a-zA-Z0-9_+]*\),/s_\1[\1],/
s/,\([A-Z][a-zA-Z][a-zA-Z][a-zA-Z0-9_+]*\)/,s_\1[\1]/
s/\([ID].C\) *\([A-Z][^ ][^ ][a-zA-Z0-9+]*\)/\1 s_\2[\2]/
s/[+].*\[/[/
/RemoveNewInt9:/,/CLC/s/\[\([^0]\)/[cs:\1/
*****************************************************************
p***@nospam.demon.co.uk
2017-02-18 12:57:21 UTC
Permalink
Post by Mike Gonta
SPACE INVADERS by PAUL S. REID Version 1.1
An Exact Clone of the Arcade Game in 100% Assembler
Game, Source Code, and Documentation Included
Freely Distributable - No Shareware Fee
Released September 17, 1995 - MINOR BUG FIXES
Ever so slightly updated version to assemble with FASM / NASM
Bootable and completely stand alone 8086 compatible assembly
It certainly is targeted at the 8086, as shown by the multiple
SHR/L reg,1 instead of a single SHR/L reg,N available on the 286 (or
possibly 186). That said there are several instances of code like
this

BlitAll: MOVSW
LOOP BlitAll

rather than the more compact

REP MOVSW

Does anyone know if there is there a reason for that? On a 8086 is
the former version faster? Or was REP buggy and best avoided, e.g. if
interrupted? I have a vague and distant recollection that mixing REP
with SEG or LOCK prefixes was problematic but that was _way_ back...

Of academic interest only - I won't lose sleep over it :-)

Pete
--
Believe those who are seeking the truth.
Doubt those who find it. - André Gide
Robert Wessel
2017-02-19 04:38:10 UTC
Permalink
Post by p***@nospam.demon.co.uk
Post by Mike Gonta
SPACE INVADERS by PAUL S. REID Version 1.1
An Exact Clone of the Arcade Game in 100% Assembler
Game, Source Code, and Documentation Included
Freely Distributable - No Shareware Fee
Released September 17, 1995 - MINOR BUG FIXES
Ever so slightly updated version to assemble with FASM / NASM
Bootable and completely stand alone 8086 compatible assembly
It certainly is targeted at the 8086, as shown by the multiple
SHR/L reg,1 instead of a single SHR/L reg,N available on the 286 (or
possibly 186). That said there are several instances of code like
this
BlitAll: MOVSW
LOOP BlitAll
rather than the more compact
REP MOVSW
Does anyone know if there is there a reason for that? On a 8086 is
the former version faster? Or was REP buggy and best avoided, e.g. if
interrupted? I have a vague and distant recollection that mixing REP
with SEG or LOCK prefixes was problematic but that was _way_ back...
Of academic interest only - I won't lose sleep over it :-)
I can't see a general one. Especially on the 8088, the REP MOVSW
would be faster, except for a loop count of 1. There *is* a problem
with the interruptible string instruction if more than one prefix is
present (IOW REP plus anything else), but the above sample don't have
that. OTOH, if you did need a segment override, a loop like your
first example was one workaround.

The one place where the loop might be faster is if the typical loop
count (cx) were 1, then it's 32 clocks vs. 34, although that doesn't
consider the extra instruction byte that would need to get fetched. So
marginal at best. With a count of two, you're at 76 nominal clocks,
plus an extra five instruction byte fetches, vs. 59 clocks for the REP
form.
Loading...