Tag Archives: retro

Soldering a 6502 PCB

A while ago I started a soldering a 6502 bare SBC.

Note pin 1 is not connected, VPB (vector pull is not supported on this PCB. But i’m planning to design a new one anyway.)

I got it running now.

It has an EPROM with Wozmon and Basic for now.
I have to redo the address decoder, but I like the simple serial interface by Geoffrey. (I hate the PIC18F15Q41, made by Microchip, but still the best minimal option .. for now)

Probably the last time i’ve used a pic was in 1998

PL/M-86

I’ve posted in the past something about pl/m.
Today i got this running again in a dosbox.

The PL/M programming language (an acronym of Programming Language for Microcomputers) is a high-level language conceived and developed by Gary Kildall in 1973 for Intel’s microprocessors.

A link to information about Gary, and ebook (pdf) he wrote.

We learned to program PL/M at school (MTS)

Below the compiler and lib files

https://media.henriaanstoot.nl/plm86.zip

Example program Tic Tac Toe I wrote in 1990

Compiling a PLM source code

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
PLM86 PROGRAM.PLM
LINK86 PROGRAM.OBJ, PLM\DOSLIBS.LIB, PLM\UTILS.LIB TO %1.LNK INITCODE
LINK PROGRAM.LNK;;;
PLM86 PROGRAM.PLM LINK86 PROGRAM.OBJ, PLM\DOSLIBS.LIB, PLM\UTILS.LIB TO %1.LNK INITCODE LINK PROGRAM.LNK;;;
PLM86 PROGRAM.PLM
LINK86 PROGRAM.OBJ, PLM\DOSLIBS.LIB, PLM\UTILS.LIB TO %1.LNK INITCODE
LINK PROGRAM.LNK;;;

Tic Tac Toe in PLM

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bke:do;
/*DOEL: */
/*Dit programma is boter kaas en eieren voor twee */
/*spelers, er wordt gecontroleerd of iemand gewonnen */
/*heeft. (Je speelt niet tegen de computer) */
/*UPDATE:12/2/90,15/2/90,18/2/90 RELDATE:19/2/90 */
/*PROGRAMMER:H.M.Aanstoot */
/*UPDATE 5/3/90 1:13:23 */
/*De volgende 4 regels zorgen ervoor dat de compiler */
/*de PLM DOS,UTIL routines die op disk staan */
/*meestuurt naar de linker */
/* bla bla 2de versie met STRINGS!! eindelijk gelukt */
$include(plm\doslibs.inc)
$include(plm\doslibs.dcl)
$include(plm\utils.dcl)
dcl naam(3) pointer;
dcl plaats(9) word;
dcl teken(2) pointer;
dcl aanzet word;
dcl loop word;
dcl a word;
dcl winnaar word;
dcl nummer word;
dcl item word;
dcl error_status word;
spelerzet:procedure;
call dsso(naam(aanzet));
call dsso(@(', geef een getal: $'));
invoer:
nummer=dsin;
nummer=nummer-48;
if nummer<1 or nummer>9 then goto invoer;
if plaats(nummer)<>0 then goto invoer;
call dso(nummer+48);
plaats(nummer)=aanzet;
end spelerzet;
update:procedure;
item=1;
call dsso(@(cr,lf,'+-----+-----+-----+',cr,lf,eos));
call dsso(@('| | | |',cr,lf,eos));
call dso(124);call zet;call dso(124);call zet;call dso(124);call zet;
call dsso(@(124,cr,lf,eos));
call dsso(@('| | | |',cr,lf,eos));
call dsso(@('+-----+-----+-----+',cr,lf,eos));
call dsso(@('| | | |',cr,lf,eos));
call dso(124);call zet;call dso(124);call zet;call dso(124);call zet;
call dsso(@(124,cr,lf,eos));
call dsso(@('| | | |',cr,lf,eos));
call dsso(@('+-----+-----+-----+',cr,lf,eos));
call dsso(@('| | | |',cr,lf,eos));
call dso(124);call zet;call dso(124);call zet;call dso(124);call zet;
call dsso(@(124,cr,lf,eos));
call dsso(@('| | | |',cr,lf,eos));
call dsso(@('+-----+-----+-----+',cr,lf,eos));
call dsso(@(' 1 2 3',cr,lf,eos));
call dsso(@(' 4 5 6',cr,lf,eos));
call dsso(@(' 7 8 9',cr,lf,eos));
end update;
zet:procedure;
if plaats(item)=0 then call dsso(@(' $'));
if plaats(item)=1 then call dsso(@(' X $'));
if plaats(item)=2 then call dsso(@(' O $'));
item=item+1;
end zet;
check:procedure;
do a=1 to 2;
if plaats(1)=a and plaats(2)=a and plaats(3)=a then winnaar=a;
if plaats(4)=a and plaats(5)=a and plaats(6)=a then winnaar=a;
if plaats(7)=a and plaats(8)=a and plaats(9)=a then winnaar=a;
if plaats(1)=a and plaats(4)=a and plaats(7)=a then winnaar=a;
if plaats(2)=a and plaats(5)=a and plaats(8)=a then winnaar=a;
if plaats(3)=a and plaats(6)=a and plaats(9)=a then winnaar=a;
if plaats(1)=a and plaats(5)=a and plaats(9)=a then winnaar=a;
if plaats(3)=a and plaats(5)=a and plaats(7)=a then winnaar=a;
end;
end check;
hoofdprogramma:
winnaar=3;
naam(1)=@('Speler 1$');
naam(2)=@('Speler 2$');
naam(3)=@('Niemand$');
do a=1 to 9; plaats(a)=0; end;
teken(1)=@('kruisje$');
teken(2)=@('rondje$');
aanzet=1;
do loop=1 to 9;
call update;
call check;
if winnaar<>3 then goto gewonnen;
call spelerzet;
aanzet=3-aanzet;
end;
call update;
gewonnen:
call dsso(naam(winnaar));
call dsso(@(' heeft gewonnen',cr,lf,eos));
if winnaar=3 then call dsso(@('Helaas, pindakaas!$'));
else call dsso(@('Gefeliciteerd ermee!$'));
call dexit(error_status);
end;
bke:do; /*DOEL: */ /*Dit programma is boter kaas en eieren voor twee */ /*spelers, er wordt gecontroleerd of iemand gewonnen */ /*heeft. (Je speelt niet tegen de computer) */ /*UPDATE:12/2/90,15/2/90,18/2/90 RELDATE:19/2/90 */ /*PROGRAMMER:H.M.Aanstoot */ /*UPDATE 5/3/90 1:13:23 */ /*De volgende 4 regels zorgen ervoor dat de compiler */ /*de PLM DOS,UTIL routines die op disk staan */ /*meestuurt naar de linker */ /* bla bla 2de versie met STRINGS!! eindelijk gelukt */ $include(plm\doslibs.inc) $include(plm\doslibs.dcl) $include(plm\utils.dcl) dcl naam(3) pointer; dcl plaats(9) word; dcl teken(2) pointer; dcl aanzet word; dcl loop word; dcl a word; dcl winnaar word; dcl nummer word; dcl item word; dcl error_status word; spelerzet:procedure; call dsso(naam(aanzet)); call dsso(@(', geef een getal: $')); invoer: nummer=dsin; nummer=nummer-48; if nummer<1 or nummer>9 then goto invoer; if plaats(nummer)<>0 then goto invoer; call dso(nummer+48); plaats(nummer)=aanzet; end spelerzet; update:procedure; item=1; call dsso(@(cr,lf,'+-----+-----+-----+',cr,lf,eos)); call dsso(@('| | | |',cr,lf,eos)); call dso(124);call zet;call dso(124);call zet;call dso(124);call zet; call dsso(@(124,cr,lf,eos)); call dsso(@('| | | |',cr,lf,eos)); call dsso(@('+-----+-----+-----+',cr,lf,eos)); call dsso(@('| | | |',cr,lf,eos)); call dso(124);call zet;call dso(124);call zet;call dso(124);call zet; call dsso(@(124,cr,lf,eos)); call dsso(@('| | | |',cr,lf,eos)); call dsso(@('+-----+-----+-----+',cr,lf,eos)); call dsso(@('| | | |',cr,lf,eos)); call dso(124);call zet;call dso(124);call zet;call dso(124);call zet; call dsso(@(124,cr,lf,eos)); call dsso(@('| | | |',cr,lf,eos)); call dsso(@('+-----+-----+-----+',cr,lf,eos)); call dsso(@(' 1 2 3',cr,lf,eos)); call dsso(@(' 4 5 6',cr,lf,eos)); call dsso(@(' 7 8 9',cr,lf,eos)); end update; zet:procedure; if plaats(item)=0 then call dsso(@(' $')); if plaats(item)=1 then call dsso(@(' X $')); if plaats(item)=2 then call dsso(@(' O $')); item=item+1; end zet; check:procedure; do a=1 to 2; if plaats(1)=a and plaats(2)=a and plaats(3)=a then winnaar=a; if plaats(4)=a and plaats(5)=a and plaats(6)=a then winnaar=a; if plaats(7)=a and plaats(8)=a and plaats(9)=a then winnaar=a; if plaats(1)=a and plaats(4)=a and plaats(7)=a then winnaar=a; if plaats(2)=a and plaats(5)=a and plaats(8)=a then winnaar=a; if plaats(3)=a and plaats(6)=a and plaats(9)=a then winnaar=a; if plaats(1)=a and plaats(5)=a and plaats(9)=a then winnaar=a; if plaats(3)=a and plaats(5)=a and plaats(7)=a then winnaar=a; end; end check; hoofdprogramma: winnaar=3; naam(1)=@('Speler 1$'); naam(2)=@('Speler 2$'); naam(3)=@('Niemand$'); do a=1 to 9; plaats(a)=0; end; teken(1)=@('kruisje$'); teken(2)=@('rondje$'); aanzet=1; do loop=1 to 9; call update; call check; if winnaar<>3 then goto gewonnen; call spelerzet; aanzet=3-aanzet; end; call update; gewonnen: call dsso(naam(winnaar)); call dsso(@(' heeft gewonnen',cr,lf,eos)); if winnaar=3 then call dsso(@('Helaas, pindakaas!$')); else call dsso(@('Gefeliciteerd ermee!$')); call dexit(error_status); end;
bke:do;
/*DOEL:                                              */
/*Dit programma is boter kaas en eieren voor twee    */
/*spelers, er wordt gecontroleerd of iemand gewonnen */
/*heeft. (Je speelt niet tegen de computer)          */
/*UPDATE:12/2/90,15/2/90,18/2/90  RELDATE:19/2/90    */
/*PROGRAMMER:H.M.Aanstoot                            */
/*UPDATE 5/3/90 1:13:23                              */
/*De volgende 4 regels zorgen ervoor dat de compiler */
/*de PLM  DOS,UTIL routines die op disk staan        */
/*meestuurt naar de linker                           */
/* bla bla 2de versie met STRINGS!! eindelijk gelukt */

$include(plm\doslibs.inc)
$include(plm\doslibs.dcl)
$include(plm\utils.dcl)
dcl naam(3)           pointer;
dcl plaats(9)         word;
dcl teken(2)          pointer;
dcl aanzet            word;
dcl loop              word;
dcl a                 word;
dcl winnaar           word;
dcl nummer            word;
dcl item              word;
dcl error_status      word;

spelerzet:procedure;
call dsso(naam(aanzet));
call dsso(@(', geef een getal: $'));
invoer:
nummer=dsin;
nummer=nummer-48;
if nummer<1 or nummer>9 then goto invoer;
if plaats(nummer)<>0 then goto invoer;
call dso(nummer+48);
plaats(nummer)=aanzet;
end spelerzet;

update:procedure;
item=1;
call dsso(@(cr,lf,'+-----+-----+-----+',cr,lf,eos));
call dsso(@('|     |     |     |',cr,lf,eos));
call dso(124);call zet;call dso(124);call zet;call dso(124);call zet;
 call dsso(@(124,cr,lf,eos));
call dsso(@('|     |     |     |',cr,lf,eos));
call dsso(@('+-----+-----+-----+',cr,lf,eos));
call dsso(@('|     |     |     |',cr,lf,eos));
call dso(124);call zet;call dso(124);call zet;call dso(124);call zet;
 call dsso(@(124,cr,lf,eos));
call dsso(@('|     |     |     |',cr,lf,eos));
call dsso(@('+-----+-----+-----+',cr,lf,eos));
call dsso(@('|     |     |     |',cr,lf,eos));
call dso(124);call zet;call dso(124);call zet;call dso(124);call zet;
 call dsso(@(124,cr,lf,eos));
call dsso(@('|     |     |     |',cr,lf,eos));
call dsso(@('+-----+-----+-----+',cr,lf,eos));

  call dsso(@('    1   2   3',cr,lf,eos));
  call dsso(@('    4   5   6',cr,lf,eos));
  call dsso(@('    7   8   9',cr,lf,eos));
end update;


zet:procedure;
if plaats(item)=0 then call dsso(@('     $'));
if plaats(item)=1 then call dsso(@('  X  $'));
if plaats(item)=2 then call dsso(@('  O  $'));
item=item+1;
end zet;

check:procedure;
   do a=1 to 2;
   if plaats(1)=a and plaats(2)=a and plaats(3)=a then winnaar=a;
   if plaats(4)=a and plaats(5)=a and plaats(6)=a then winnaar=a;
   if plaats(7)=a and plaats(8)=a and plaats(9)=a then winnaar=a;

   if plaats(1)=a and plaats(4)=a and plaats(7)=a then winnaar=a;
   if plaats(2)=a and plaats(5)=a and plaats(8)=a then winnaar=a;
   if plaats(3)=a and plaats(6)=a and plaats(9)=a then winnaar=a;

   if plaats(1)=a and plaats(5)=a and plaats(9)=a then winnaar=a;
   if plaats(3)=a and plaats(5)=a and plaats(7)=a then winnaar=a;
   end;
end check;


hoofdprogramma:
winnaar=3;
naam(1)=@('Speler 1$');
naam(2)=@('Speler 2$');
naam(3)=@('Niemand$');
do a=1 to 9; plaats(a)=0; end;
teken(1)=@('kruisje$');
teken(2)=@('rondje$');
aanzet=1;

    do loop=1 to 9;
    call update;
    call check;
    if winnaar<>3 then goto gewonnen;
    call spelerzet;
    aanzet=3-aanzet;
    end;
    
call update;
gewonnen:
call dsso(naam(winnaar));
call dsso(@(' heeft gewonnen',cr,lf,eos));
if winnaar=3 then call dsso(@('Helaas, pindakaas!$'));
			 else call dsso(@('Gefeliciteerd ermee!$'));

call dexit(error_status);
end;

Went to HCC Retro Meeting. (SDK-85 Update)

UPDATE 20240104

I’ve met Dirk, he brought his SDK85.
And demo-ed a working cassette interface, using audio cables and a second laptop.

Saw a presentation about the MC14500B a weird little chip, robots, retro rebuilds using Raspberries.

Update 20240104 some soldering

Meanwhile .. my old MicroTapes are not using the Intel AP-29 method for data, so that needs some work also.

SDK-85 interface PCB soldered

Using spacers, I can use the existing holes in the SDK to hold the interface PCB in place. Now I have to move my information sheet. 🙂

My scope didn’t save the test capture .. Next time.
But I could see the clear ones and zeros.

You can faintly see the data, stop start of a program is visible.

Dirk cleaned up a OCR version of the program, which I cleaned up some more, and found some errors.
So that should be okay now.

Some old and some new demo stuff

A dentro (Combination of demo and intro.) from 1995

It using only background colors behind existing text to display the dentro text. There is (somewhere) a version with animations.

New work:

Booting from floppy, showing a flash screen in mode 13h.
Starting a trackloader, which loads a raw adlib file and plays it.
All sectors written to floppy using my new sector writer.
WOOT .. music at boot time!

Sector writer

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
use16
org 0100h
; 0 0 1
; bootblock with flashcode
mov cl,1 ; start
mov al,1 ; # sectors
mov dh,0 ; head
mov bx,bootblock
call wrtsector
; flash image
mov cl,8
mov al,4
mov dh,0
mov bx,gfx
call wrtsector
; Music loader
mov cl,7 ; start
mov al,1 ; # sectors
mov dh,0 ; head
mov bx,nextpart
call wrtsector
; Music raw
mov cl,13 ; start
mov al,4 ; # sectors
mov dh,0 ; head
mov bx,musicraw
call wrtsector
jmp do_exit
printerror:
push cs ; make ds same as cs
pop ds
MOV DX,TxtErr1 ; error
MOV AH,09h
INT 21h
mov ax,4c00h
int 21h
do_exit:
mov ax,4c00h
int 21h
wrtsector:
; On entry: AH 03h
; AL Number of sectors to write
; CH Cylinder number (10-bit value; upper 2 bits
; in CL)
; CL Starting sector number
; DH Head number
; DL Drive number
; ES:BX Address of memory buffer
;
; Returns: AH Status of operation (See Service 01h)
; AL Number of sectors written
; CF Set if error, else cleared
cld
mov ah, 3h ; int13h function 2
mov ch, 0 ; from cylinder number 0
mov dl,0
push cs
pop es
int 13h
jc printerror
ret
TxtErr1: DB "Error!",7,10,13,"$"
bootblock:
file 'flash_b.bin'
gfx:
file 'flashgfx.raw'
nextpart:
file 'nextpart.bin'
musicraw:
file 'LVLINTRO.RAW'
use16 org 0100h ; 0 0 1 ; bootblock with flashcode mov cl,1 ; start mov al,1 ; # sectors mov dh,0 ; head mov bx,bootblock call wrtsector ; flash image mov cl,8 mov al,4 mov dh,0 mov bx,gfx call wrtsector ; Music loader mov cl,7 ; start mov al,1 ; # sectors mov dh,0 ; head mov bx,nextpart call wrtsector ; Music raw mov cl,13 ; start mov al,4 ; # sectors mov dh,0 ; head mov bx,musicraw call wrtsector jmp do_exit printerror: push cs ; make ds same as cs pop ds MOV DX,TxtErr1 ; error MOV AH,09h INT 21h mov ax,4c00h int 21h do_exit: mov ax,4c00h int 21h wrtsector: ; On entry: AH 03h ; AL Number of sectors to write ; CH Cylinder number (10-bit value; upper 2 bits ; in CL) ; CL Starting sector number ; DH Head number ; DL Drive number ; ES:BX Address of memory buffer ; ; Returns: AH Status of operation (See Service 01h) ; AL Number of sectors written ; CF Set if error, else cleared cld mov ah, 3h ; int13h function 2 mov ch, 0 ; from cylinder number 0 mov dl,0 push cs pop es int 13h jc printerror ret TxtErr1: DB "Error!",7,10,13,"$" bootblock: file 'flash_b.bin' gfx: file 'flashgfx.raw' nextpart: file 'nextpart.bin' musicraw: file 'LVLINTRO.RAW'
use16
org 0100h
; 0 0 1
; bootblock with flashcode
	mov cl,1  ; start
	mov al,1  ; # sectors
	mov dh,0  ; head
	mov bx,bootblock
	call wrtsector

; flash image
	mov cl,8
	mov al,4
	mov dh,0
	mov bx,gfx
	call wrtsector

; Music loader
	mov cl,7  ; start
	mov al,1  ; # sectors
	mov dh,0  ; head
	mov bx,nextpart
	call wrtsector

; Music raw
	mov cl,13  ; start
	mov al,4  ; # sectors
	mov dh,0  ; head
	mov bx,musicraw
	call wrtsector

	jmp do_exit

printerror:
	push cs		; make ds same as cs
	pop ds
	MOV DX,TxtErr1	; error
	MOV AH,09h
	INT 21h
	mov ax,4c00h
	int 21h
do_exit:
	mov ax,4c00h
	int 21h
wrtsector:
;       On entry:      AH         03h
;                      AL         Number of sectors to write
;                      CH         Cylinder number (10-bit value; upper 2 bits
;                                 in CL)
;                      CL         Starting sector number
;                      DH         Head number
;                      DL         Drive number
;                      ES:BX      Address of memory buffer
; 
;       Returns:       AH         Status of operation (See Service 01h)
;                      AL         Number of sectors written
;                      CF         Set if error, else cleared

	cld
	mov ah, 3h    ; int13h function 2
	mov ch, 0     ; from cylinder number 0
	mov dl,0
	push cs 
	pop es
	int 13h
	jc printerror
ret

TxtErr1:	 DB "Error!",7,10,13,"$"

bootblock:
	file 'flash_b.bin'
gfx:
	file 'flashgfx.raw'
nextpart:
	file 'nextpart.bin'
musicraw:
	file 'LVLINTRO.RAW' 

I wanted to know how a floppy differs from a floppy.
So i wrote below code to fill each sector on a floppy disk or image with information at the start of each sector.
(Head, Cylinder and Sector)

empty.bin was made using
dd if=/dev/zero of=empty.bin count=1 bs=512

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
use16
org 0100h
mov ch,0 ; cyl
mov cl,1 ; sector
mov dh,0 ; head
nextsector:
mov bx,empty
mov [empty],dh
mov [empty+1],ch
mov [empty+2],cl
push cx
push ax
push dx
call printer
call wrtsector
pop dx
pop ax
pop ax
inc cl
cmp cl,19
jnz nextsector
mov cl,1
inc ch
cmp ch,79
jnz nextsector
; other side
mov dh,1
mov ch,0
mov cl,1
nextsector1:
mov bx,empty
mov [empty],dh
mov [empty+1],ch
mov [empty+2],cl
push cx
push ax
push dx
call wrtsector
pop dx
pop ax
pop ax
inc cl
cmp cl,19
jnz nextsector1
mov cl,1
inc ch
cmp ch,79
jnz nextsector1
jmp do_exit
printerror:
push cs ; make ds same as cs
pop ds
MOV AX,3 ; default text mode 3
INT 10h
MOV DX,TxtErr1 ; error
MOV AH,09h
INT 21h
mov ax,4c00h
int 21h
do_exit:
mov ax,3
int 10h
mov ax,4c00h
int 21h
printer:
push cx
push dx
mov dl,dh
mov ah, 02h
add dl, 30h
int 21h
mov dl,ch
add dl, 30h
int 21h
mov dl,cl
add dl, 30h
int 21h
mov dx,13
mov ah,2
int 21h
mov dx,10
mov ah,2
int 21h
pop dx
pop cx
ret
wrtsector:
; On entry: AH 03h
; AL Number of sectors to write
; CH Cylinder number (10-bit value; upper 2 bits
; in CL)
; CL Starting sector number
; DH Head number
; DL Drive number
; ES:BX Address of memory buffer
;
; Returns: AH Status of operation (See Service 01h)
; AL Number of sectors written
; CF Set if error, else cleared
;
cld
mov ah, 3h ; int13h function 2
mov al,1
mov dl,0
push cs
pop es
int 13h
jc printerror
ret
TxtErr1: DB "Error!",7,10,13,"$"
empty:
file 'empty.bin'
use16 org 0100h mov ch,0 ; cyl mov cl,1 ; sector mov dh,0 ; head nextsector: mov bx,empty mov [empty],dh mov [empty+1],ch mov [empty+2],cl push cx push ax push dx call printer call wrtsector pop dx pop ax pop ax inc cl cmp cl,19 jnz nextsector mov cl,1 inc ch cmp ch,79 jnz nextsector ; other side mov dh,1 mov ch,0 mov cl,1 nextsector1: mov bx,empty mov [empty],dh mov [empty+1],ch mov [empty+2],cl push cx push ax push dx call wrtsector pop dx pop ax pop ax inc cl cmp cl,19 jnz nextsector1 mov cl,1 inc ch cmp ch,79 jnz nextsector1 jmp do_exit printerror: push cs ; make ds same as cs pop ds MOV AX,3 ; default text mode 3 INT 10h MOV DX,TxtErr1 ; error MOV AH,09h INT 21h mov ax,4c00h int 21h do_exit: mov ax,3 int 10h mov ax,4c00h int 21h printer: push cx push dx mov dl,dh mov ah, 02h add dl, 30h int 21h mov dl,ch add dl, 30h int 21h mov dl,cl add dl, 30h int 21h mov dx,13 mov ah,2 int 21h mov dx,10 mov ah,2 int 21h pop dx pop cx ret wrtsector: ; On entry: AH 03h ; AL Number of sectors to write ; CH Cylinder number (10-bit value; upper 2 bits ; in CL) ; CL Starting sector number ; DH Head number ; DL Drive number ; ES:BX Address of memory buffer ; ; Returns: AH Status of operation (See Service 01h) ; AL Number of sectors written ; CF Set if error, else cleared ; cld mov ah, 3h ; int13h function 2 mov al,1 mov dl,0 push cs pop es int 13h jc printerror ret TxtErr1: DB "Error!",7,10,13,"$" empty: file 'empty.bin'
use16
org 0100h

	mov ch,0  ; cyl
	mov cl,1  ; sector
	mov dh,0  ; head
nextsector:
	mov bx,empty
	mov [empty],dh
	mov [empty+1],ch
	mov [empty+2],cl
	push cx
	push ax
	push dx
	call printer
	call wrtsector
	pop dx
	pop ax
	pop ax
	inc cl
	cmp cl,19
	jnz nextsector
	mov cl,1
	inc ch
	cmp ch,79
	jnz nextsector
; other side
	mov dh,1
	mov ch,0
	mov cl,1

nextsector1:
	mov bx,empty
	mov [empty],dh
	mov [empty+1],ch
	mov [empty+2],cl
	push cx
	push ax
	push dx
	call wrtsector
	pop dx
	pop ax
	pop ax
	inc cl
	cmp cl,19
	jnz nextsector1
	mov cl,1
	inc ch
	cmp ch,79
	jnz nextsector1


	jmp do_exit

printerror:
	push cs		; make ds same as cs
	pop ds
	MOV AX,3	; default text mode 3
	INT 10h
	MOV DX,TxtErr1	; error
	MOV AH,09h
	INT 21h
	mov ax,4c00h
	int 21h


do_exit:
	mov ax,3
	int 10h
	mov ax,4c00h
	int 21h

printer:
	push cx
	push dx
	mov dl,dh
	mov ah, 02h
	add dl, 30h
	int 21h
	mov dl,ch
	add dl, 30h
	int 21h
	mov dl,cl 
	add dl, 30h
	int 21h
	mov dx,13
  mov ah,2
  int 21h  
  mov dx,10
  mov ah,2
  int 21h
	pop dx
	pop cx
	ret

wrtsector:
;       On entry:      AH         03h
;                      AL         Number of sectors to write
;                      CH         Cylinder number (10-bit value; upper 2 bits
;                                 in CL)
;                      CL         Starting sector number
;                      DH         Head number
;                      DL         Drive number
;                      ES:BX      Address of memory buffer
; 
;       Returns:       AH         Status of operation (See Service 01h)
;                      AL         Number of sectors written
;                      CF         Set if error, else cleared
;

	cld
	mov ah, 3h    ; int13h function 2
	mov al,1
	mov dl,0
	push cs 
	pop es
	int 13h
	jc printerror
ret

TxtErr1:	 DB "Error!",7,10,13,"$"

empty:
	file 'empty.bin'

Viewing the floppy image with ghex

Offset 7000 = Head 1, Cylinder 1 and sector 3

When doing
times 512 – ($-$$) db 0
to fill binaries to 512 bytes, you could cat the sectors to a disk/file with this knowledge.

80×86 boot demo generic work plus code optimalisation and tricks

Writing tools and effects for my new boot demo.

  • Started a generic sector read/writer
  • Some effects
  • A sin/cos data writer to include into your source
  • Working on a library of functions (sector loaders, color palette, vert/hor retrace functions)
  • Laying out a memory map for the demo

Below the output of the sin/cos generator ( see used in video below )
(It also shows a visual plot of the function)

Source code python script

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# importing the required module
import matplotlib.pyplot as plt
import numpy as np
import math
# Change these
numberofdatapoints = 360
maxamp = 180
howmuchfromwave = 0.5
numberofharmonies = 1
# Number of harmonies are sin/cos additions in the calculation line below
# not here
step = 360/numberofdatapoints*howmuchfromwave
offset = maxamp
maxamp = maxamp / numberofharmonies
offset = 0
x = [ ]
for xv in range(numberofdatapoints):
xvstep=xv * step
# Calculation line
# datapoint=np.sin(math.radians(xvstep))
# Double harmony example
datapoint=np.sin(math.radians(xvstep)) + (np.sin(math.radians(xvstep*3))/2)
datapoint=datapoint * maxamp
datapoint=datapoint + offset
x.append(int(datapoint))
print(" db ", end="")
print(*x, sep = ",")
# plotting the points
plt.plot(x)
# naming the x axis
plt.xlabel('x - axis')
# naming the y axis
plt.ylabel('y - axis')
# giving a title to my graph
plt.title('Example')
# function to show the plot
plt.show()
# importing the required module import matplotlib.pyplot as plt import numpy as np import math # Change these numberofdatapoints = 360 maxamp = 180 howmuchfromwave = 0.5 numberofharmonies = 1 # Number of harmonies are sin/cos additions in the calculation line below # not here step = 360/numberofdatapoints*howmuchfromwave offset = maxamp maxamp = maxamp / numberofharmonies offset = 0 x = [ ] for xv in range(numberofdatapoints): xvstep=xv * step # Calculation line # datapoint=np.sin(math.radians(xvstep)) # Double harmony example datapoint=np.sin(math.radians(xvstep)) + (np.sin(math.radians(xvstep*3))/2) datapoint=datapoint * maxamp datapoint=datapoint + offset x.append(int(datapoint)) print(" db ", end="") print(*x, sep = ",") # plotting the points plt.plot(x) # naming the x axis plt.xlabel('x - axis') # naming the y axis plt.ylabel('y - axis') # giving a title to my graph plt.title('Example') # function to show the plot plt.show()
# importing the required module
import matplotlib.pyplot as plt
import numpy as np
import math

# Change these
numberofdatapoints = 360
maxamp = 180
howmuchfromwave = 0.5
numberofharmonies = 1
# Number of harmonies are sin/cos additions in the calculation line below

# not here
step = 360/numberofdatapoints*howmuchfromwave
offset = maxamp
maxamp = maxamp / numberofharmonies
offset = 0


x = [ ]

for xv in range(numberofdatapoints):
    xvstep=xv * step
# Calculation line
#    datapoint=np.sin(math.radians(xvstep))
# Double harmony example
    datapoint=np.sin(math.radians(xvstep)) + (np.sin(math.radians(xvstep*3))/2)

    datapoint=datapoint * maxamp
    datapoint=datapoint + offset
    x.append(int(datapoint))

print("    db ", end="")
print(*x, sep = ",") 
  
# plotting the points 
plt.plot(x)
  
# naming the x axis
plt.xlabel('x - axis')
# naming the y axis
plt.ylabel('y - axis')
  
# giving a title to my graph
plt.title('Example')
  
# function to show the plot
plt.show()

Minimalistic very fast boot loader flash screen effect

Graffiti bouncher test (probably ends up bounching a 320×400 image)
This one uses the generated sintab (Using the python script above)

Test code for a text scroller

Code optimalisation/tricks

clear a (double) register?
xor ax,ax
is faster than
mov ax,0h

Want to make ds pointer same as cs?
Instead of
mov ax,cs
mov ds,ax
use
push cs
pop ds

self modifying code
mostly we just move data around, but you also can change the runtime code (instructions)

  • a – increment ax on line 103h
  • b – another part of the code/maybe in a interrupt
    10Fh load al with 48h (thats the opcode for decrement (see c)
    111h place the opcode in address 103h, which had 40h ..
    Now we changed the code to decrement next time

Speedcode/unrolled code

Populair on the C64 where resources are even more limited, you could use speedcode.
Most of the speedcode you generate, due to its repeating lines.
When looking at clock cycles you can save some extra cycles, by using a little more memory but saving on “expensive” loops.

Simple example

Left a funtion with a loop, right is the same but all instuctions sequencial

Left 15 bytes but 284 cycles

Right 39 bytes but only 102 cycles!

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
4
4
; below part 9 times
9
3
4
16 or 4
= 284 cycles
Speedcode
4
2 ; xor is faster
9
3 ; even 2 when you can use BX pair!
9
3
9
3
9
3
9
3
9
3
9
3
9
3
9
= 111 cycles (or 102 BX pair)
4 4 ; below part 9 times 9 3 4 16 or 4 = 284 cycles Speedcode 4 2 ; xor is faster 9 3 ; even 2 when you can use BX pair! 9 3 9 3 9 3 9 3 9 3 9 3 9 3 9 = 111 cycles (or 102 BX pair)
4
4
; below part 9 times
9
3
4
16 or 4
= 284 cycles

Speedcode
4
2 ; xor is faster
9
3 ; even 2 when you can use BX pair!
9
3
9
3
9
3
9
3
9
3
9
3
9
3
9
= 111 cycles (or 102 BX pair)

Moving memory blocks (No DMA)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
;DS:(E)SI to address ES:(E)DI
push cs ; example to set es to code segment
pop es
mov si,1000 ; offset pointer source
xor di,di ; destination offset
mov cx,320*100 ; number of transfers (See below words)
mov ax,0a000h ; Destination
mov es,ax ; destination segment
cld ; Clear direction flag set SI and DI to auto-increment
rep movsw ; repeat mov words! until number of transfers is 0
;
;DS:(E)SI to address ES:(E)DI push cs ; example to set es to code segment pop es mov si,1000 ; offset pointer source xor di,di ; destination offset mov cx,320*100 ; number of transfers (See below words) mov ax,0a000h ; Destination mov es,ax ; destination segment cld ; Clear direction flag set SI and DI to auto-increment rep movsw ; repeat mov words! until number of transfers is 0 ;
;DS:(E)SI to address ES:(E)DI
    push cs          ; example to set es to code segment
    pop es
    mov si,1000      ; offset pointer source
    xor di,di        ; destination offset
    mov cx,320*100   ; number of transfers (See below words)
    mov ax,0a000h    ; Destination
    mov es,ax        ; destination segment 
    cld              ; Clear direction flag set SI and DI to auto-increment
    rep movsw        ; repeat mov words! until number of transfers is 0
;  

Short binary > bcd > dec (ascii) convert for numbers (0-99)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mov ax,01ch ; = 28
mov bx,0ah ; = 10
div bl ; divide BL by AX
; AX = 0802 ; Remainder/Divider
xchg ah,al ; change around (dont use if you want to keep little endian)
add ax,3030h ; offset to ascii 30=0 31=1
; ax ends up with 3238 .. 28
mov ax,01ch ; = 28 mov bx,0ah ; = 10 div bl ; divide BL by AX ; AX = 0802 ; Remainder/Divider xchg ah,al ; change around (dont use if you want to keep little endian) add ax,3030h ; offset to ascii 30=0 31=1 ; ax ends up with 3238 .. 28
mov ax,01ch ; = 28 
mov bx,0ah ; = 10
div bl ; divide BL by AX
       ; AX = 0802 ; Remainder/Divider
xchg ah,al ; change around (dont use if you want to keep little endian)
add ax,3030h ; offset to ascii 30=0 31=1
             ; ax ends up with 3238 .. 28 

Melting effect in 8086, using only register manipulation

(NOTE, Dosbox can’t cope with the register speed, use real HW or PCem)

Effect using a edited photo I made from fireworks ..

Generating a RAW image and Palette, a in a new way

This bash script to convert BMP to Raw and a compiled colorpalette.
(Note: this converts to 8 bit depth, the assembly code in the final assemby program converts to 6 for VGA mode 13h

So this time, i won´t have to use the standard VGA palette as mentioned in previous posts.
(Gimp colors > indexed (255 colors) ; save as BMP, exclude colorspace information)

I’m using identify to extract the colorpalette, which i’m converting to DB entries for the fasm compiler

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#!/bin/bash
if [ $# -lt 1 ] ; then
echo "$0 filename"
exit 0
fi
size=$(stat $1 | grep Size | awk '{ print $2 }')
skipsize=$(( $size - 64000))
dd if=$1 of=$1.raw skip=$skipsize bs=1
identify -verbose $1 | awk '/Colormap:/,/Rendering/' | grep -v Colormap | grep -v Rendering | awk '{ print $2 } ' | tr -d '()' | while read ; do echo "db $REPLY" ;done > data.asm
fasm data.asm
#!/bin/bash if [ $# -lt 1 ] ; then echo "$0 filename" exit 0 fi size=$(stat $1 | grep Size | awk '{ print $2 }') skipsize=$(( $size - 64000)) dd if=$1 of=$1.raw skip=$skipsize bs=1 identify -verbose $1 | awk '/Colormap:/,/Rendering/' | grep -v Colormap | grep -v Rendering | awk '{ print $2 } ' | tr -d '()' | while read ; do echo "db $REPLY" ;done > data.asm fasm data.asm
#!/bin/bash
if [ $# -lt 1 ] ; then
	echo "$0 filename"
	exit 0
fi
size=$(stat $1 | grep Size | awk '{ print $2 }')
skipsize=$(( $size - 64000))
dd if=$1 of=$1.raw skip=$skipsize bs=1

identify -verbose $1 | awk '/Colormap:/,/Rendering/' | grep -v Colormap | grep -v Rendering | awk '{ print $2 } ' | tr -d '()' | while read ; do echo "db $REPLY" ;done > data.asm
fasm data.asm

Code

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
use16
org 0x100
; variables
CRTC_INDEX = 0x03D4
CRTC_DATA = 0x03D5
INPUT_STATUS = 0x03DA
HRETRACE = 0x01
VRETRACE = 0x03 ; bit 3 =8 ?
MAXIMUM_SCAN_LINE = 0x09
LINE_OFFSET = 0x13
; bar
upperbar = 1
lowerbar = 399
jmp start
; memory locations for data
updown dw 1
direction DB 0
filename DB "firework.raw",0
oldline db 0
start:
; set mode 320x200 256 colors palette
mov ah,0x0
mov al,13h
int 10h
; clear screen routine, not really needed
clearscreen:
push ax
mov ax, 0a000h
mov es, ax
pop ax
xor di, di
inc ax
mov cx, 64000 ; 320x200
rep stosb
; set colors
; call file loader
call Loadfile
call setpalette
; Move loaded file to Screen memory
mov ax,0a000h
mov es,ax
mov ax,6000h
mov ds,ax
mov si,0
mov di,0
mov cx,320*200/2
rep movsw
push cs
pop ds
; store org effect2 values
mov dx, CRTC_INDEX
mov al,LINE_OFFSET
out dx,al
mov dx, CRTC_DATA
in al,dx
mov [oldline],al
; after displaying the image or displaying an error, wait for keypress to exit
waitforkeyloop:
call effect ; Calling the effect
MOV AH,1
INT 16h
JZ waitforkeyloop
XOR AH,AH
INT 16h
Exit:
MOV AX,3 ; default text mode 3
INT 10h
MOV AX,4C00h ; exit to dos (terminate process)
INT 21h
; loop ends here
; Loads raw 64000 bytes image to screen memory
Loadfile:
push ds
MOV DX,filename
MOV AX,3D00h ; open filehandle
INT 21h
JC Err1
MOV BX,AX ; filehandle
MOV CX,64000
mov dx,06000h ; destination 0000:a000h - Screen memory
mov ds,dx
MOV DX,0
MOV AH,3Fh ; read from file
INT 21h
JC Err1
MOV AH,3Eh ; close filehandle
INT 21h
pop ds
RET
; print error
Err1:
push cs ; make ds same as cs
pop ds
MOV AX,3 ; default text mode 3
INT 10h
MOV DX,TxtErr1 ; error
MOV AH,09h
INT 21h
RET
effect:
cli ; stop interrupts
call waitvretrace ; wait for vertical retrace
; gets start scanline and direction
mov ax,[updown]
mov cl,[direction]
cmp cl,0 ; 0 move down
jz addcounter
dec ax
dec ax
cmp ax,upperbar ; reached upper bar ?
jnz gohere ; jnz
mov cl,0
mov [direction],cl
jmp gohere
addcounter:
inc ax
inc ax
cmp ax,lowerbar ; reached bottom bar?
jnz gohere ;jnz
mov cl,1 ; change direction
mov [direction],cl
gohere:
mov [updown],ax ; store new location
; al = scanline, call wait for scanline
call longwaithretrace
; other effect
mov dx, CRTC_INDEX
mov al, LINE_OFFSET
out dx,al
mov dx, CRTC_DATA
mov al, 0
out dx,al
; wait scanlines (height of bar)
mov ax,400
mov cx,[updown]
sub ax,cx
call longwaithretrace
; restore effect2
mov dx, CRTC_INDEX
mov al, LINE_OFFSET
out dx, al
mov dx, CRTC_DATA
mov al, [oldline]
out dx,al
sti ; start interrupts again
ret
; routine that fixes 8 to 6 bits and sets palette
setpalette:
; 8 bits to 6
mov si,coltab
mov cx,256*3
rest:
mov al,[si]
shr al,2
mov [si],al
dec cx
inc si
cmp cx,0
jnz rest
; now set colors
mov dx,3c8h
xor al,al
out dx,al
inc dx
mov si,coltab
mov cx,256*3
rep outsb
ret
; this waits for vertical retrace
waitvretrace:
mov dx,INPUT_STATUS
waitv1:
in al,dx
test al,8
jnz waitv1
waitv2:
in al,dx
test al,8
jz waitv2
ret
; routine that waits for horizontal retrace
waithretrace:
mov cl,al
mov dx,INPUT_STATUS
waith1:
in al,dx
test al,1
jnz waith1
waith2:
in al,dx
test al,1
jz waith2
dec cl
cmp cl,0
jnz waith1
ret
longwaithretrace:
mov cx,ax
mov dx,INPUT_STATUS
lwaith1:
in al,dx
test al,1
jnz lwaith1
lwaith2:
in al,dx
test al,1
jz lwaith2
dec cx
cmp cx,0
jnz lwaith1
ret
TxtErr1 DB "firework.raw not found!",7,10,13,"$"
coltab:
include 'data.asm'
use16 org 0x100 ; variables CRTC_INDEX = 0x03D4 CRTC_DATA = 0x03D5 INPUT_STATUS = 0x03DA HRETRACE = 0x01 VRETRACE = 0x03 ; bit 3 =8 ? MAXIMUM_SCAN_LINE = 0x09 LINE_OFFSET = 0x13 ; bar upperbar = 1 lowerbar = 399 jmp start ; memory locations for data updown dw 1 direction DB 0 filename DB "firework.raw",0 oldline db 0 start: ; set mode 320x200 256 colors palette mov ah,0x0 mov al,13h int 10h ; clear screen routine, not really needed clearscreen: push ax mov ax, 0a000h mov es, ax pop ax xor di, di inc ax mov cx, 64000 ; 320x200 rep stosb ; set colors ; call file loader call Loadfile call setpalette ; Move loaded file to Screen memory mov ax,0a000h mov es,ax mov ax,6000h mov ds,ax mov si,0 mov di,0 mov cx,320*200/2 rep movsw push cs pop ds ; store org effect2 values mov dx, CRTC_INDEX mov al,LINE_OFFSET out dx,al mov dx, CRTC_DATA in al,dx mov [oldline],al ; after displaying the image or displaying an error, wait for keypress to exit waitforkeyloop: call effect ; Calling the effect MOV AH,1 INT 16h JZ waitforkeyloop XOR AH,AH INT 16h Exit: MOV AX,3 ; default text mode 3 INT 10h MOV AX,4C00h ; exit to dos (terminate process) INT 21h ; loop ends here ; Loads raw 64000 bytes image to screen memory Loadfile: push ds MOV DX,filename MOV AX,3D00h ; open filehandle INT 21h JC Err1 MOV BX,AX ; filehandle MOV CX,64000 mov dx,06000h ; destination 0000:a000h - Screen memory mov ds,dx MOV DX,0 MOV AH,3Fh ; read from file INT 21h JC Err1 MOV AH,3Eh ; close filehandle INT 21h pop ds RET ; print error Err1: push cs ; make ds same as cs pop ds MOV AX,3 ; default text mode 3 INT 10h MOV DX,TxtErr1 ; error MOV AH,09h INT 21h RET effect: cli ; stop interrupts call waitvretrace ; wait for vertical retrace ; gets start scanline and direction mov ax,[updown] mov cl,[direction] cmp cl,0 ; 0 move down jz addcounter dec ax dec ax cmp ax,upperbar ; reached upper bar ? jnz gohere ; jnz mov cl,0 mov [direction],cl jmp gohere addcounter: inc ax inc ax cmp ax,lowerbar ; reached bottom bar? jnz gohere ;jnz mov cl,1 ; change direction mov [direction],cl gohere: mov [updown],ax ; store new location ; al = scanline, call wait for scanline call longwaithretrace ; other effect mov dx, CRTC_INDEX mov al, LINE_OFFSET out dx,al mov dx, CRTC_DATA mov al, 0 out dx,al ; wait scanlines (height of bar) mov ax,400 mov cx,[updown] sub ax,cx call longwaithretrace ; restore effect2 mov dx, CRTC_INDEX mov al, LINE_OFFSET out dx, al mov dx, CRTC_DATA mov al, [oldline] out dx,al sti ; start interrupts again ret ; routine that fixes 8 to 6 bits and sets palette setpalette: ; 8 bits to 6 mov si,coltab mov cx,256*3 rest: mov al,[si] shr al,2 mov [si],al dec cx inc si cmp cx,0 jnz rest ; now set colors mov dx,3c8h xor al,al out dx,al inc dx mov si,coltab mov cx,256*3 rep outsb ret ; this waits for vertical retrace waitvretrace: mov dx,INPUT_STATUS waitv1: in al,dx test al,8 jnz waitv1 waitv2: in al,dx test al,8 jz waitv2 ret ; routine that waits for horizontal retrace waithretrace: mov cl,al mov dx,INPUT_STATUS waith1: in al,dx test al,1 jnz waith1 waith2: in al,dx test al,1 jz waith2 dec cl cmp cl,0 jnz waith1 ret longwaithretrace: mov cx,ax mov dx,INPUT_STATUS lwaith1: in al,dx test al,1 jnz lwaith1 lwaith2: in al,dx test al,1 jz lwaith2 dec cx cmp cx,0 jnz lwaith1 ret TxtErr1 DB "firework.raw not found!",7,10,13,"$" coltab: include 'data.asm'
use16
org 0x100

; variables
CRTC_INDEX = 0x03D4
CRTC_DATA = 0x03D5
INPUT_STATUS = 0x03DA
HRETRACE = 0x01
VRETRACE = 0x03 ; bit 3 =8 ?
MAXIMUM_SCAN_LINE = 0x09
LINE_OFFSET = 0x13

; bar
upperbar = 1
lowerbar = 399

jmp start
; memory locations for data
updown dw 1
direction DB 0
filename DB "firework.raw",0
oldline db 0


start:
; set mode 320x200 256 colors palette
	mov ah,0x0
	mov al,13h
	int 10h

; clear screen routine, not really needed
clearscreen:
	push ax
	mov ax, 0a000h
	mov es, ax
	pop ax
	xor di, di
	inc ax
	mov cx, 64000 ; 320x200
	rep stosb
; set colors
; call file loader 
	call Loadfile
	call setpalette

; Move loaded file to Screen memory
	mov ax,0a000h
	mov es,ax
	mov ax,6000h
	mov ds,ax
	mov si,0
	mov di,0
	mov cx,320*200/2
	rep movsw

	push cs
	pop ds

;	store org effect2 values
	mov dx, CRTC_INDEX
	mov al,LINE_OFFSET
	out dx,al
	mov dx, CRTC_DATA
	in al,dx
	mov [oldline],al


; after displaying the image or displaying an error, wait for keypress to exit
waitforkeyloop:
	call effect 	; Calling the effect
	MOV AH,1
	INT 16h
	JZ waitforkeyloop
	XOR AH,AH
	INT 16h
Exit:
	MOV AX,3	; default text mode 3
	INT 10h
	MOV AX,4C00h	; exit to dos (terminate process)
	INT 21h
; loop ends here

; Loads raw 64000 bytes image to screen memory
Loadfile:
	push ds
	MOV DX,filename
	MOV AX,3D00h	; open filehandle
	INT 21h
	JC Err1
	MOV BX,AX   	; filehandle
	MOV CX,64000
	mov dx,06000h 	; destination 0000:a000h - Screen memory
	mov ds,dx
	MOV DX,0
	MOV AH,3Fh	; read from file
	INT 21h
	JC  Err1
	MOV AH,3Eh	; close filehandle
	INT 21h
	pop ds
RET

; print error
Err1:
	push cs		; make ds same as cs
	pop ds
	MOV AX,3	; default text mode 3
	INT 10h
	MOV DX,TxtErr1	; error
	MOV AH,09h
	INT 21h
	RET

effect:
	cli		; stop interrupts
	call waitvretrace	; wait for vertical retrace

; gets start scanline and direction
	mov ax,[updown]
	mov cl,[direction]
	cmp cl,0		; 0 move down
	jz	addcounter
	dec ax
	dec ax
	cmp ax,upperbar  	; reached upper bar ?
	jnz gohere ; jnz
	mov cl,0
	mov [direction],cl
	jmp gohere
addcounter:
	inc ax
	inc ax
	cmp ax,lowerbar		; reached bottom bar?
	jnz gohere 	;jnz
	mov cl,1		; change direction
	mov [direction],cl
gohere:
	mov [updown],ax		; store new location

; al = scanline, call wait for scanline
	call longwaithretrace
; other effect
        mov dx, CRTC_INDEX
        mov al, LINE_OFFSET
        out dx,al
        mov dx, CRTC_DATA
        mov al, 0
        out dx,al


; wait scanlines (height of bar)
	mov ax,400
	mov cx,[updown]
	sub ax,cx
	call longwaithretrace

; restore effect2
        mov dx, CRTC_INDEX
        mov al, LINE_OFFSET
        out dx, al
        mov dx, CRTC_DATA
        mov al, [oldline]
        out dx,al
		
	sti	; start interrupts again
	ret

; routine that fixes 8 to 6 bits and sets palette
setpalette:
	; 8 bits to 6 
	mov si,coltab
	mov cx,256*3
	rest:
		mov al,[si]
		shr al,2
		mov [si],al
		dec cx
		inc si
		cmp cx,0
		jnz rest
; now set colors
	mov dx,3c8h
	xor al,al
	out dx,al
	inc dx
	mov si,coltab
	mov cx,256*3
	rep outsb
ret


; this waits for vertical retrace
waitvretrace:
	mov dx,INPUT_STATUS
	waitv1:
		in al,dx
		test al,8
		jnz waitv1
	waitv2:
		in al,dx
		test al,8
		jz waitv2
ret

; routine that waits for horizontal retrace
waithretrace:
	mov cl,al
	mov dx,INPUT_STATUS
	waith1:
		in al,dx
		test al,1
		jnz waith1
	waith2:
		in al,dx
		test al,1
		jz waith2
		dec cl
		cmp cl,0
		jnz waith1
ret

longwaithretrace:
	mov cx,ax
	mov dx,INPUT_STATUS
	lwaith1:
		in al,dx
		test al,1
		jnz lwaith1
	lwaith2:
		in al,dx
		test al,1
		jz lwaith2
		dec cx
		cmp cx,0
		jnz lwaith1
ret
TxtErr1 DB "firework.raw not found!",7,10,13,"$"

coltab: 
include 'data.asm'

Micro Adlib player in Assembly

Plays RAW adlib songs in 100 lines of code … kindda

Using information from here:
https://moddingwiki.shikadi.net/wiki/RAW_Format_(Adlib)

And using fasm to compile I can play captured raw songs.

But something is still off ?!?

It sounds a little different, and I need to implement a better timer routine. (Below my version and opencubicplayer)

This is a test for my bootloader, playing music from my bootblock!

CODE

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
use16
org 0x100
ctrlreg=0388h
datareg=0389h
mainloop:
; set speed (Byte 8,9 from the raw file)
mov ax,[tune+8]
mov [clockspeed],ax
; call player
call rawreg
; wait 0.5 sec for exit
mov bx,6
call waitmore
jmp exit
rawreg:
mov bx,tune+0ah ; start of song at offset ah
; order registerdata, register!
; Are there more control codes? ???
lraw:
mov cx,[bx]
; reg = 2 - check data
cmp ch,2
je checkreg2
; reg = 0 - cyclewait
cmp ch,0
je cyclewait
; data = FFFF - end song - end play routine
cmp cx,0ffffh
jne skipr
ret
cyclewait: ; waits cl times waitroutine
cylloop:
call waitlong
dec cl
jnz cylloop
inc bx
inc bx
jmp lraw
checkreg2:
; check low opl
cmp cl,1
jne checkh
mov ch,0
mov [highlow],ch
jmp incandret
checkh:
; check high opl
cmp cl,2
jne check00
mov ch,1
mov [highlow],ch
jmp incandret
check00:
; set new speed
cmp cl,0
jne incandret
inc bx
inc bx
mov ax,[bx]
mov [clockspeed],ax
incandret:
; next double byte in the song please
inc bx
inc bx
jmp lraw
skipr:
; sends data to the control and data registers
mov dx,ctrlreg
mov al,[highlow]
cmp al,0
je regokay
inc dx
inc dx
regokay:
mov al,ch
out dx,al
; call waitshort ; not needed for newer adlib cards
mov dx,datareg
mov al,[highlow]
cmp al,0
je regokay2
inc dx
inc dx
regokay2:
mov al,cl
out dx,al
call waitlong
inc bx
inc bx
jmp lraw
waitshort:
push ax
push cx
push dx
mov cx, 0 ;HIGH WORD.
mov dx, 010h ;LOW WORD.
mov ah, 86h ;WAIT.
int 15h
pop dx
pop cx
pop ax
ret
waitlong:
push bx
push ax
push cx
push dx
mov cx, 0 ;HIGH WORD.
mov dx, [clockspeed]
shr dx,1
mov ah, 86h ;WAIT.
int 15h
pop dx
pop cx
pop ax
pop bx
ret
waitmore:
; in bx == 12h is 1 sec
; destroys ax,bx,cx,dx
push ax
push bx
push cx
push dx
mov ax,0h
int 1ah
add dx, bx
mov bx,dx
waitloop:
mov ax,0h
int 1ah
cmp bx,dx
jnz waitloop
pop dx
pop cx
pop bx
pop ax
ret
exit:
mov dx,0388h
mov al,0b0h
out dx,al
inc dx
xor al,al
out dx,al
mov ax,04c00h
int 21h
clockspeed: dw 0
highlow: db 0
tune:
file 'RAWSONG.RAW'
use16 org 0x100 ctrlreg=0388h datareg=0389h mainloop: ; set speed (Byte 8,9 from the raw file) mov ax,[tune+8] mov [clockspeed],ax ; call player call rawreg ; wait 0.5 sec for exit mov bx,6 call waitmore jmp exit rawreg: mov bx,tune+0ah ; start of song at offset ah ; order registerdata, register! ; Are there more control codes? ??? lraw: mov cx,[bx] ; reg = 2 - check data cmp ch,2 je checkreg2 ; reg = 0 - cyclewait cmp ch,0 je cyclewait ; data = FFFF - end song - end play routine cmp cx,0ffffh jne skipr ret cyclewait: ; waits cl times waitroutine cylloop: call waitlong dec cl jnz cylloop inc bx inc bx jmp lraw checkreg2: ; check low opl cmp cl,1 jne checkh mov ch,0 mov [highlow],ch jmp incandret checkh: ; check high opl cmp cl,2 jne check00 mov ch,1 mov [highlow],ch jmp incandret check00: ; set new speed cmp cl,0 jne incandret inc bx inc bx mov ax,[bx] mov [clockspeed],ax incandret: ; next double byte in the song please inc bx inc bx jmp lraw skipr: ; sends data to the control and data registers mov dx,ctrlreg mov al,[highlow] cmp al,0 je regokay inc dx inc dx regokay: mov al,ch out dx,al ; call waitshort ; not needed for newer adlib cards mov dx,datareg mov al,[highlow] cmp al,0 je regokay2 inc dx inc dx regokay2: mov al,cl out dx,al call waitlong inc bx inc bx jmp lraw waitshort: push ax push cx push dx mov cx, 0 ;HIGH WORD. mov dx, 010h ;LOW WORD. mov ah, 86h ;WAIT. int 15h pop dx pop cx pop ax ret waitlong: push bx push ax push cx push dx mov cx, 0 ;HIGH WORD. mov dx, [clockspeed] shr dx,1 mov ah, 86h ;WAIT. int 15h pop dx pop cx pop ax pop bx ret waitmore: ; in bx == 12h is 1 sec ; destroys ax,bx,cx,dx push ax push bx push cx push dx mov ax,0h int 1ah add dx, bx mov bx,dx waitloop: mov ax,0h int 1ah cmp bx,dx jnz waitloop pop dx pop cx pop bx pop ax ret exit: mov dx,0388h mov al,0b0h out dx,al inc dx xor al,al out dx,al mov ax,04c00h int 21h clockspeed: dw 0 highlow: db 0 tune: file 'RAWSONG.RAW'
use16
org 0x100

ctrlreg=0388h
datareg=0389h


mainloop:
	; set speed (Byte 8,9 from the raw file)
	mov ax,[tune+8]
	mov [clockspeed],ax
	; call player
	call rawreg
	; wait 0.5 sec for exit
	mov bx,6
	call waitmore
	jmp exit

rawreg:
	mov bx,tune+0ah		; start of song at offset ah

; order registerdata, register!
; Are there more control codes? ???

lraw:
	mov cx,[bx]
; reg = 2 - check data
	cmp ch,2
	je checkreg2
; reg = 0 - cyclewait
	cmp ch,0
	je cyclewait
; data = FFFF - end song - end play routine
	cmp cx,0ffffh
	jne skipr
	ret

cyclewait:		; waits cl times waitroutine
cylloop:	
	call waitlong
	dec cl
	jnz cylloop
	inc bx
	inc bx
	jmp lraw

checkreg2:
; check low opl
	cmp cl,1
	jne checkh
	mov ch,0
	mov [highlow],ch
	jmp incandret
checkh:
; check high opl
	cmp cl,2
	jne check00
	mov ch,1
	mov [highlow],ch
	jmp incandret
check00:
; set new speed
	cmp cl,0
	jne incandret
	inc bx
	inc bx
	mov ax,[bx]
	mov [clockspeed],ax
	
incandret:
; next double byte in the song please
	inc bx     
	inc bx     
	jmp lraw

skipr:
; sends data to the control and data registers
	mov dx,ctrlreg
	mov al,[highlow]
	cmp al,0
	je regokay
	inc dx
	inc dx
regokay:
	mov al,ch
	out dx,al
;	call waitshort ; not needed for newer adlib cards
	mov dx,datareg
	mov al,[highlow]
	cmp al,0
	je regokay2
	inc dx
	inc dx
regokay2:
	mov al,cl
	out dx,al
	call waitlong
	inc bx
	inc bx
	jmp lraw

waitshort:
	push ax
	push cx
	push dx
	mov cx, 0      ;HIGH WORD.
	mov dx, 010h ;LOW WORD.
	mov ah, 86h    ;WAIT.
	int 15h
	pop dx
	pop cx
	pop ax
	ret

waitlong:
	push bx
	push ax
	push cx
	push dx
	mov cx, 0      ;HIGH WORD.
	mov dx, [clockspeed]
	shr dx,1
	mov ah, 86h    ;WAIT.
	int 15h
	pop dx
	pop cx
	pop ax
	pop bx
	ret

waitmore:
; in bx == 12h is 1 sec
; destroys ax,bx,cx,dx
	push ax
	push bx
	push cx
	push dx
	mov ax,0h
	int 1ah
	add dx, bx
	mov bx,dx
waitloop:
	mov ax,0h
	int 1ah
	cmp bx,dx
	jnz waitloop
	pop dx
	pop cx
	pop bx
	pop ax
	ret

exit:
	mov dx,0388h
	mov al,0b0h
	out dx,al
	inc dx
	xor al,al
	out dx,al
	mov ax,04c00h
	int 21h

clockspeed: dw 0

highlow:	db 0

tune:
	file 'RAWSONG.RAW'

Rewrote 8086 bootblock trackloader

Loading a 320×200 image from 14 cilinders. There is no msdos on the floppy!

9 sectors, 14 cilinders, 1 head * sector size (512 bytes) = 64512 bytes

Mode 13h (320×200 265 colors)

Cuting the raw part from a BMP (see previous post)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
root@battlestation:/mnt/# ls -la MAD.bmp
-rw-rw-r-- 1 fash fash 65078 Sep 14 15:57 MAD.bmp
I need 64000 bytes (320x200)
65078-64000 = 1078
root@battlestation:/mnt/# dd if=MAD.bmp of=mad.raw skip=1078 bs=1
64000+0 records in
64000+0 records out
64000 bytes (64 kB, 62 KiB) copied, 0.441618 s, 145 kB/s
root@battlestation:/mnt/# ls -la MAD.bmp -rw-rw-r-- 1 fash fash 65078 Sep 14 15:57 MAD.bmp I need 64000 bytes (320x200) 65078-64000 = 1078 root@battlestation:/mnt/# dd if=MAD.bmp of=mad.raw skip=1078 bs=1 64000+0 records in 64000+0 records out 64000 bytes (64 kB, 62 KiB) copied, 0.441618 s, 145 kB/s
root@battlestation:/mnt/# ls -la MAD.bmp
-rw-rw-r-- 1 fash fash 65078 Sep 14 15:57 MAD.bmp

I need 64000 bytes (320x200)
65078-64000 = 1078

root@battlestation:/mnt/# dd if=MAD.bmp of=mad.raw skip=1078 bs=1
64000+0 records in
64000+0 records out
64000 bytes (64 kB, 62 KiB) copied, 0.441618 s, 145 kB/s

I use debug to write to the sectors

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
debug mad.raw
-w100 0 9 7f
(write from address 100 drive=0 startsector=9 (cylinder 1) and 7f sectors long
debug mad.raw -w100 0 9 7f (write from address 100 drive=0 startsector=9 (cylinder 1) and 7f sectors long
debug mad.raw
-w100 0 9 7f
(write from address 100 drive=0 startsector=9 (cylinder 1) and 7f sectors long

Wrote a little sector viewer to debug/view data written.

  • r – read sector again
  • s – next sector (shift previous)
  • c – next cylinder (shift previous)
  • h – toggle head 0 – 1
  • p – load palette from current 2 sectors
  • l – clear screen
  • 1 – goto graphic mode
  • 2 – goto text mode and show sector,head and cylinder info
  • q – quit
  • -/+ tweak palette offset ( was needed for debugging

I will post the code after some code cleaning and adding some comments

Boot loader with image 320×200 256 colors.

This is a work in progress, below are my Lab notes.

I want to rewrite pieces we made for a demo, loading images and effects from a floppydisk bootloader.

Without looking at old code (which was written using Masm), I wanted to learn the steps using Fasm.

I started with a boot sector program, It should do the following.

  • Set graphic mode, and start a trackloader
  • Load sector 2 and 3 which contains the color palette for the image.
  • Next sectors, cylinders and heads contain the raw image

I got it working, half that is.
In the past I used real disks, and now a virtual disk, maybe thats the difference?

First Code

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
use16
org 0x7c00
mov ah,0x0
mov al,0x13
int 10h
mov ax, 0a000h
mov es, ax
xor di, di
mov ax, 50
mov cx, 64000
rep stosb
loophere:
jmp loophere
times 510 - ($-$$) db 0
dw 0xaa55
use16 org 0x7c00 mov ah,0x0 mov al,0x13 int 10h mov ax, 0a000h mov es, ax xor di, di mov ax, 50 mov cx, 64000 rep stosb loophere: jmp loophere times 510 - ($-$$) db 0 dw 0xaa55
use16
org 0x7c00

mov ah,0x0
mov al,0x13
int 10h

mov ax, 0a000h
mov es, ax
xor di, di
mov ax, 50
mov cx, 64000
rep stosb

loophere:
	jmp loophere


times 510 - ($-$$) db 0

dw 0xaa55

Compiling and starting:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
fasm mybootblock.asm
qemu-system-x86_64 --drive format=raw,file=mybootblock.bin
fasm mybootblock.asm qemu-system-x86_64 --drive format=raw,file=mybootblock.bin
fasm mybootblock.asm
qemu-system-x86_64 --drive format=raw,file=mybootblock.bin

This works, it sets the graphical mode and clears the screen.

Second Code
Skipping the int 25h version

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
## Track read part
xor ax, ax ; DS = 0
mov ds, ax
cld
mov ah, 2h ; int13h function 2 track read
mov al, 2 ; number of tracks ( should be 2 for reading only palette)
mov ch, 0 ; from cylinder number 0
mov cl, 2 ; the sector number 2 - second sector (starts from 1, not 0)
mov dh, 0 ; head number 0
xor bx, bx ; BX = 0
mov es, bx ; ES = 0
mov bx, 7e00h ; Offset from above
int 13h
call setpal
## End part with setpalette routine
## appending palette.colors
## and a raw image
setpal:
mov dx,3c8h
xor al,al
mov di, ax
out dx,al
inc dx
mov cx,256*3
mov si,07e00h
rep outsb
ret
times 510 - ($-$$) db 0
dw 0xaa55
include 'palette.colors'
times 2048 - ($-$$) db 0
file 'image.raw'
## Track read part xor ax, ax ; DS = 0 mov ds, ax cld mov ah, 2h ; int13h function 2 track read mov al, 2 ; number of tracks ( should be 2 for reading only palette) mov ch, 0 ; from cylinder number 0 mov cl, 2 ; the sector number 2 - second sector (starts from 1, not 0) mov dh, 0 ; head number 0 xor bx, bx ; BX = 0 mov es, bx ; ES = 0 mov bx, 7e00h ; Offset from above int 13h call setpal ## End part with setpalette routine ## appending palette.colors ## and a raw image setpal: mov dx,3c8h xor al,al mov di, ax out dx,al inc dx mov cx,256*3 mov si,07e00h rep outsb ret times 510 - ($-$$) db 0 dw 0xaa55 include 'palette.colors' times 2048 - ($-$$) db 0 file 'image.raw'
## Track read part
    xor ax, ax    ; DS = 0
    mov ds, ax
    cld
    mov ah, 2h    ; int13h function 2 track read
    mov al, 2     ; number of tracks ( should be 2 for reading only palette)
    mov ch, 0     ; from cylinder number 0
    mov cl, 2     ; the sector number 2 - second sector (starts from 1, not 0)
    mov dh, 0     ; head number 0
    xor bx, bx    ; BX = 0
    mov es, bx    ; ES = 0
    mov bx, 7e00h ; Offset from above
    int 13h

    call setpal

## End part with setpalette routine
## appending palette.colors
## and a raw image

setpal:	
    mov dx,3c8h
    xor al,al
    mov di, ax
    out dx,al
    inc dx
    mov cx,256*3
    mov si,07e00h
    rep outsb
    ret
times 510 - ($-$$) db 0

dw 0xaa55
include 'palette.colors'

times 2048 - ($-$$) db 0
file 'image.raw'

Seems there is still a header on the RAW file, lets look at how I made this.

NOTE! .. Below converts an image with a STANDARD VGA palette, not a custom one as used above

Looking with ghex at the file I saw that there was a header 0x415 bytes large.
(Probably still palette colors in there)

dd if=shoes.bmp of=cutshoe.bmp bs=1 skip=1078 (0x415h + 3?)
worked for me

Loading the extra tracks didn’t work for me?!?!
But how could I define tracks/sectors and heads on a virtual floppy?

I tried to write sectors using debug.com

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
start dosbox
imgmount a: /tmp/floppy.img -t floppy
debug.com bootsector.bin
-r bx 01
-r cx 512
; set bx:cx for size
-w 100 0 0 1
; write from address 100, drive 0 (a), sector 0, number of sectors
; testing
-l 100 0 0 1
;load sector to addr 100 drive 0 sector 0 number of sectors
This used to work with real disks on a real machine, not in dosbox ?!?!
my way to create a disk in linux
dd if=bootblock.bin of=disk1.img bs=512 count=1 seek=0
dd if=palette.col of=disk1.img bs=512 count=1 seek=1 # or 2?
dd if=shoes.raw of=disk1.img bs=512 count=10000 seek=17
start dosbox imgmount a: /tmp/floppy.img -t floppy debug.com bootsector.bin -r bx 01 -r cx 512 ; set bx:cx for size -w 100 0 0 1 ; write from address 100, drive 0 (a), sector 0, number of sectors ; testing -l 100 0 0 1 ;load sector to addr 100 drive 0 sector 0 number of sectors This used to work with real disks on a real machine, not in dosbox ?!?! my way to create a disk in linux dd if=bootblock.bin of=disk1.img bs=512 count=1 seek=0 dd if=palette.col of=disk1.img bs=512 count=1 seek=1 # or 2? dd if=shoes.raw of=disk1.img bs=512 count=10000 seek=17
start dosbox
imgmount a: /tmp/floppy.img -t floppy
debug.com bootsector.bin
-r bx 01 

-r cx 512
; set bx:cx for size
-w 100 0 0 1
; write from address 100, drive 0 (a), sector 0, number of sectors

; testing
-l 100 0 0 1
;load sector to addr 100 drive 0 sector 0 number of sectors 

This used to work with real disks on a real machine, not in dosbox ?!?!

my way to create a disk in linux

dd if=bootblock.bin of=disk1.img bs=512 count=1 seek=0
dd if=palette.col of=disk1.img bs=512 count=1 seek=1 # or 2?
dd if=shoes.raw of=disk1.img bs=512 count=10000 seek=17

It looks like I can’t read futher than 18 sectors on a virtual floppy.
What next? Head=1? Cylinder=1?
Below the info from a floppy image before altering.

DOS/MBR boot sector, code offset 0x3c+2, OEM-ID “MSDOS5.0”, root entries 224, sectors 2880 (volumes <=32 MB), sectors/FAT 9, sectors/track 18, serial number 0x1c2a0d0f, unlabeled, FAT (12 bit), followed by FAT

Appending the RAW to a executable gave me problems to.
(Without making a bootdisk)

Above and below weird data. Appending the data to the executable needs some work also.

At least for today let me display this image correctly 🙂

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
use16
org 0x100
; set mode 320x200 256 colors palette
mov ah,0x0
mov al,13h
int 10h
; clear screen routine, not really needed
clearscreen:
push ax
mov ax, 0a000h
mov es, ax
pop ax
xor di, di
inc ax
mov cx, 64000 ; 320x200
rep stosb
; call file loader
call Loadfile
; after displaying the image or displaying an error, wait for keypress to exit
waitforkeyloop:
MOV AH,1
INT 16h
JZ waitforkeyloop
XOR AH,AH
INT 16h
Exit:
MOV AX,3 ; default text mode 3
INT 10h
MOV AX,4C00h ; exit to dos (terminate process)
INT 21h
Loadfile:
MOV DX,filename
MOV AX,3D00h ; open filehandle
INT 21h
JC Err1
MOV BX,AX ; filehandle
MOV CX,0FFFFh ; size
mov dx,0a000h ; destination 0000:a000h - Screen memory
mov ds,dx
MOV DX,0
MOV AH,3Fh ; read from file
INT 21h
JC Err1
MOV AH,3Eh ; close filehandle
INT 21h
RET
; print error
Err1:
push cs ; make ds same as cs
pop ds
MOV DX,TxtErr1 ; error
MOV AH,09h
INT 21h
RET
filename DB "shoes.bmp",0
TxtErr1 DB "shoes.bmp not found!",7,10,13,"$"
use16 org 0x100 ; set mode 320x200 256 colors palette mov ah,0x0 mov al,13h int 10h ; clear screen routine, not really needed clearscreen: push ax mov ax, 0a000h mov es, ax pop ax xor di, di inc ax mov cx, 64000 ; 320x200 rep stosb ; call file loader call Loadfile ; after displaying the image or displaying an error, wait for keypress to exit waitforkeyloop: MOV AH,1 INT 16h JZ waitforkeyloop XOR AH,AH INT 16h Exit: MOV AX,3 ; default text mode 3 INT 10h MOV AX,4C00h ; exit to dos (terminate process) INT 21h Loadfile: MOV DX,filename MOV AX,3D00h ; open filehandle INT 21h JC Err1 MOV BX,AX ; filehandle MOV CX,0FFFFh ; size mov dx,0a000h ; destination 0000:a000h - Screen memory mov ds,dx MOV DX,0 MOV AH,3Fh ; read from file INT 21h JC Err1 MOV AH,3Eh ; close filehandle INT 21h RET ; print error Err1: push cs ; make ds same as cs pop ds MOV DX,TxtErr1 ; error MOV AH,09h INT 21h RET filename DB "shoes.bmp",0 TxtErr1 DB "shoes.bmp not found!",7,10,13,"$"
use16
org 0x100

; set mode 320x200 256 colors palette
	mov ah,0x0
	mov al,13h
	int 10h

; clear screen routine, not really needed
clearscreen:
	push ax
	mov ax, 0a000h
	mov es, ax
	pop ax
	xor di, di
	inc ax
	mov cx, 64000 ; 320x200
	rep stosb

; call file loader 
	call Loadfile

; after displaying the image or displaying an error, wait for keypress to exit
waitforkeyloop:
	MOV AH,1
	INT 16h
	JZ waitforkeyloop
	XOR AH,AH
	INT 16h
Exit:
	MOV AX,3	; default text mode 3
	INT 10h
	MOV AX,4C00h	; exit to dos (terminate process)
	INT 21h

Loadfile:
	MOV DX,filename
	MOV AX,3D00h	; open filehandle
	INT 21h
	JC Err1
	MOV BX,AX   	; filehandle
	MOV CX,0FFFFh 	; size
	mov dx,0a000h 	; destination 0000:a000h - Screen memory
	mov ds,dx

	MOV DX,0
	MOV AH,3Fh	; read from file
	INT 21h
	JC  Err1
	MOV AH,3Eh	; close filehandle
	INT 21h

	RET
; print error
Err1:
	push cs		; make ds same as cs
	pop ds
	MOV DX,TxtErr1	; error
	MOV AH,09h
	INT 21h
	RET

filename DB "shoes.bmp",0
TxtErr1 DB "shoes.bmp not found!",7,10,13,"$"

Tomorrow .. back to the track loader