; Minesweeper 1K ZX81 ; The classic Minesweepergame now for the 1K ZX81 ; 9× 9 fields = 10 mines. Beginner ; 16×16 fields = 40 mines. Advanced ; 24×16 fields = 99 mines. Expert ; The hidden data of the fields is stored on the screen ; in executable opcodes that will take 4 tstates so the screen remains perfect ; After loading the screenmemory is used to initialize the program and copy ; parts over the sysvar that are no longer used. ; Before starting the game the screen is unpacked to its full size. ; compiled with SJASM ; 12 bytes free reused for sizetable start equ #4009 border equ 8 ; border can be changed by altering this variable bomb equ #50 ; fixed value now, but altered during development viewsp equ 27 ; fixed value now, but altered during development q equ 27 ; ASCII ZX81 org start USRcall jp init ; BASIC makes call to here d_file dw dfile ; lowres screen location ; 10 bytes free reused for dx dy table dfcc dw dfile+1 var dw vars dest dw 0 eline dw last chadd dw last-1 xptr dw 0 dw 0,0 berg db 0 mem dw 0 ; keyboardtable for userdefined dw 0,0 lastk db 255,255,255 margin db 55 nxtlin dw basic ; used to start, then overwritten for code won pop bc ; clear stack db 254 ; hide flagx flagx db 0 jr go2 ; continue taddr dw 3213 nrfld dw 0 frames dw 65535 coords db 0,0 prcc db 0 sposn db 33,24 cdflag db 64 ; hidden values on the screen ; #40 workfield display ; #50 bomb ; #51 0 bombs nearby ; #53 1 bomb ; #55 2 bombs ; #57 3 ; #59 4 ; #5b 5 ; #5d 6 ; #5f 7 ; (#61) 8 ; 8 bombs is possible, but this kills the program ; a test is coded to prevent enclosed field by bombs go2 ld hl,screen+27 ; start of screen ld bc,(maxy) ; B is maxy from each possible level disp0 ld d,255 ; show all fields within playfield inc hl disp ld a,(hl) sub border ; test end of playfield (variable per level) jr nz,show ld d,a ; but do not show outside playfield show bit 6,(hl) jr z,noshow ; already shown cp #51-border ; is it space? jr z,showsp ; if so, then no calculation from screenvalue cp bomb-border ; is it bomb? jr nz,shtest ; if not then calculation from screenvalue ld a,23+23+25-border ; bomb character as screen 'should' be defb #c2 ; JP NZ, hide showsp showsp ld a,viewsp+viewsp+25-border ; space character as screen 'should' be shtest sub 25-border ; through calculation to correct display rra and d ; visible or not ld (hl),a ; show character noshow inc hl ; go to next field ld a,(hl) cp 118 ; test end of line jr nz,disp inc hl ; skip Newline djnz disp0 ; do all lines game ld a,%11110111 ; read 1-5 in a,(254) cpl and 31 jr z,game ld c,255 flev inc c rra jr nc,flev ; go to right level ld a,c cp 3 jr nc,game ; skip 4 and 5 add a,a add a,c ld l,a ; point to start of table ld h,#40 ld e,(hl) ; get maxx inc hl ld d,(hl) ; get maxy inc hl ld a,(hl) ; get number of mines ld (counter+1),a ; store for later ld hl,maxy+1 ld (hl),d ; store for finding spaces inc hl inc hl ld (hl),e ; store for finding spaces push de ; save xy call digit2 ; show nr of mines defb 254 ; skip "POP AF" new pop af ; rare situation of 8 mines around 1 field pop de ; is not allowed, so make new game ; DE holds size of minefield cls ld hl,screen ; start of screen push hl ; use again later ld b,18 ; all possible lines line ld (hl),#51 ; use "invisible data" on screen inc hl ld a,(hl) cp 118 jr nz,line ; fill line with "spaces/0 mines" inc hl ; skip Newline from erase djnz line ; full screen must be cleared when previous game was larger pop hl ; get start of screen, B is now 0 x0 ld c,0 ; set C to 0, start of line ld (hl),border ; signal border xline inc hl ld a,b dec a cp d ; test top or bottom border to show jr c,nextx ld (hl),border ; set top/bottom border nextx inc c ld a,c cp e ; test right end of playfield found jr nz,xline inc hl ld (hl),border ; set right border endline inc hl ; now go to end of line ld a,(hl) cp 118 jr nz,endline inc hl ; skip Newline ld a,b dec a inc b cp d jr nz,x0 ; do sized level only counter ld a,0 ; get number of bombs setbomb push af ; save as counter push de ; save sizeborders sb2 ld a,d call rnd ; get random y-pos ld b,a ; save random y-pos ld a,e call rnd ; get random x-pos ld c,a ; save random x-pos call field cp bomb ; is it bomb? jr z,sb2 ; if so find empty position ld (hl),bomb ; set as bomb dec b ; start left top dec c call field ld bc,dfcc-1 ; dy dx table cbomb ld a,(hl) ; get current field cp b ; test border is only value less than #40 on set up jr c,skipfld ; skip border cp bomb jr z,skipfld ; skip other bombs cp #5f ; test on rare bug "inclosed field" jr z,new ; inclosed field will reset due to value inc (hl) ; increase number of adjacent bombs inc (hl) ; table needs double increase or it will not display ok skipfld inc bc ; pointer to next field ld a,(bc) ; get stepcount to next field ld e,a ld d,0 add hl,de ; calculate next field or a ; all 8 fields around counted for? jr nz,cbomb ; count bomb pop de ; maxx and maxy back pop af ; bombcounter ld b,a ; in the end B=1 dec a ; set all bombs jr nz,setbomb ld c,b ; BC = 1,1, now it is time to play gloop call field ; get current screenfield ld (hl),15 ; set "?" cursor ld d,a ; save background in D wup ld a,(lastk) ; wait for key up inc a ; instead of a timed repeat jr nz,wup push bc ; save xy w4key ld bc,(lastk) ; get last key pressed ld a,c inc a jr z,w4key ; wait for a key pressed ld (hl),d ; restore background call #7bd ; translate to ascii pop bc ; get xy push bc ; save xy ld hl,mem ; table of keys cp (hl) ; test up pressed jr nz,tdown dec b ; go up tdown inc hl cp (hl) ; test down pressed jr nz,tleft inc b ; go down tleft inc hl cp (hl) ; test left pressed jr nz,tright dec c ; go left tright inc hl cp (hl) ; test right pressed jr nz,tfire inc c ; go right tfire inc hl cp (hl) ; test show field pressed jr z,hit ; if so handle show inc hl cp (hl) ; test mark field pressed jr nz,fldtest ; if not, test possible new position call field ; get screenposition jr z,fldtest ; do not mark shown field cp bomb ; is it a bomb ld a,43+128 ; "F"lag ld (hl),a ; set flag jp nz,nxtlin ; only mark bomb, otherwise dead ld hl,counter+1 ; bomb marked, now decrease number of bombs dec (hl) ld a,(hl) push af call digit2 ; display decreased nr of mines pop af jp z,won ; all mines marked ; exit through hit is possible now field is marked as shown hit call field ; get screenposition jr z,fldtest ; already shown, test is done in field sub bomb ; test against bomb jp z,nxtlin ; gameover, lost show error field rra ; rra will NOT set zero or a ; so zero test needed ld (hl),#40 ; signal special space jr z,maxy ; enter connected spaces routine showval add a,28 ; add "0" charactercode ld (hl),a ; show number fldtest call field ; get current screenposition cp border ; is it border jr z,false ; don't move out of playfield pop af ; drop old x y gl1 jr gloop ; continue game spacin dec b ; go to left up corner from current field dec c ld (hl),viewsp ; found 'space' to visible 'space' call field ; calculate left up corner spaces ld bc,dfcc-1 ; dy dx table sf1 ld a,(hl) bit 6,a jr z,fnd2 ; only handle hidden values cp b ; holds #40 jr z,fnd2 ; do previous space later sub bomb+1 ld (hl),b ; set all fields as space jr z,fnd2 ; new space found, also set rra add a,28 ld (hl),a ; show number when needed fnd2 inc bc ld a,(bc) ld e,a add hl,de ; go to next field or a jr nz,sf1 ; show all fields around space maxy ld b,9 ; end of screen maxx ld c,9 ; end of line seeksp call field ; get screenposition sub #40 ld d,a ; preset d=0 for spacin jr z,spacin ; show a "hidden" space dec c jr nz,seeksp ; check the line djnz maxx ; check each line ; here all spaces shown false pop bc ; retrieve current correct xy jr gl1 ; continue play with a JR and JP, saves a byte digit2 ld hl,levnr ; show remaining mines ld d,27 ft inc d sub 10 jr nc,ft ; find number of tens ld (hl),d ; write TENS of number inc hl add a,38 ; undo SUB 10 and make it ASCII ld (hl),a ; write UNITS of number ; ret ; RET is skipped to save a byte. Needed to have correct display of nr mines field ld hl,screen-27 push bc ; save current xy ld a,b inc a ; undo possible zero inc a ; add 1 for dx ld b,0 ; BC holds dx fld add hl,bc ; do 1x dx and then dy ld c,27 ; BC holds dy dec a ; calculate field jr nz,fld pop bc ; get original xy ld a,(hl) ; get fieldvalue bit 6,a ; test on hidden for later ret rnd push de ; save maxy/maxx rseed ld de,0 ; get seed ld hl,(frames) ; get frames add hl,de ; double "random" dec hl ; point to next generator field ld d,a ; get boundary ld a,h and #1f ld h,a ; stay in ROM ld a,(hl) ; get "random" value ld (rseed+1),hl ; save next seed frnd sub d ; get in range jr nc,frnd adc a,d ; value in range pop de ; get maxy/maxx ret ; The screen, first used for initialization n equ 101 dfile db 118 db 118,118,118,118 levnr db "J"-q,"K"-q ; nr of mines on start writers initials db 0 db "M"+n,"I"+n,"N"+n,"E"+n,"S"+n,"W"+n db "E"+n,"E"+n,"P"+n,"E"+n,"R"+n db 118,118 screen dw 0,0,0,0,0,0,0,0,0,0,0,0,0 ; an empty line to be copied later db 118 lines db 118 dw 0,0,0,0,0,0,0,0 ; the textline dw 0,0,0,0,0,0,0,0 db 118,118 ; up down left right show mark ; 1234561234561234561234561234561234 34 characters on a line db "U"+n,"P"+n,0,0,0,0 db "D"+n,"O"+n,"W"+n,"N"+n,0,0 db "L"+n,"E"+n,"F"+n,"T"+n,0,0 db "R"+n,"I"+n,"G"+n,"H"+n,"T"+n,0 db "S"+n,"H"+n,"O"+n,"W"+n,0,0 db "M"+n,"A"+n,"R"+n,"K"+n,118,118 redkeys db "Q"-q,0,0,0,0,0 db "A"-q,0,0,0,0,0 db "O"-q,0,0,0,0,0 db "P"-q,0,0,0,0,0 db "Z"-q,0,0,0,0,0 db "F"-q,118 db 118,118,118,118,118,118,118 ; "empty" lines so part of full screen db 118,118,118,118,118,118,118 ; can be used as code on startup db 118 basic dw 0 ; basic is on screen dw 0 ; but erased before db 249,212,28 ; game starts db 126 db 143,0,18 init ld hl,game ; gamestart ld sp,#4400 ; SP on end of memory push hl ; start on stack ld hl,text ; get message msg push hl ld de,lines+1 ld bc,32 ldir ld e,(hl) ; get final character ld hl,frames ; delay ld a,(hl) sub 6 wmsg cp (hl) jr nz,wmsg pop hl inc hl ld a,e ; test last character as Newline cp 118 jr nz,msg ; scroll message ld hl,redkeys ld de,mem ; keytab upw ld a,(lastk) inc a jr nz,upw ld (hl),8 ; show key cursor dwnw ld bc,(lastk) ld a,c inc a jr z,dwnw push hl push de call #7bd ; translate in-port to key pop de ld (de),a ; save key number inc de ld bc,#7d ; translate key to ascii add a,c ld c,a ld a,(bc) ; get ascii pop hl ld (hl),a ; asciikey to screen ld c,6 add hl,bc ; point to next key ld a,(hl) cp 118 jr nz,upw ; repeat all keys ld hl,#bd36 ; "LD (HL),61+128" ld (nxtlin),hl ; reuse nxtlin as code ld hl,sizes ; sizetable each game ld de,#4000 ; copy sizetable over sysvar ld bc,9 ldir ld hl,around ; copy aroundpointers over sysvar ld de,dfcc ld c,8 ldir ld hl,screen ld de,lines ld bc,17*27 ; now built a full screen ldir jp #19f9 ; built the screen with LDIR in ROM around db 1,1,25,2,25,1,1,0 ; stepsize fields around bomb sizes db #09,09,10,#10,#10,40,#18,#10,80 ; levelsize and nr bombs text db 0 ; the scrolltext dw 0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0 db "T"-q,"H"-q,"E"-q,"Y"-q,0 db "S"-q,"A"-q,"I"-q,"D"-q,0 db "I"-q,"T"-q,0,"C"-q,"O"-q db "U"-q,"L"-q,"D"-q,0,"N"-q db "O"-q,"T"-q,0,"B"-q,"E"-q,0 db "D"-q,"O"-q,"N"-q,"E"-q,26,0 db "B"-q,"U"-q,"T"-q,0,"H"-q db "E"-q,"R"-q,"E"-q,0,"I"-q db "S"-q,0,"M"-q,"I"-q,"N"-q db "E"-q,"S"-q,"W"-q,"E"-q db "E"-q,"P"-q,"E"-q,"R"-q,0 db "I"-q,"N"-q,0,29,"K"-q,0 db "F"-q,"R"-q,"O"-q,"M"-q,0 db "J"-q,"O"-q,"H"-q,"A"-q db "N"-q,0,11,"D"-q,"R"-q,0,"B"-q db "E"-q,"E"-q,"P"-q,11,0 db "K"-q,"O"-q,"E"-q,"L"-q db "M"-q,"A"-q,"N"-q,27,0 dw 0,0,0,0,0,0 db "D"-q,"E"-q,"F"-q,"I"-q,"N"-q,"E"-q db 0,"K"-q,"E"-q,"Y"-q,"S"-q dw 0,0,0,0,0 db 118 vars db 128 last equ $ end