Code - studies on programming on the ZX Spectrum

Etudes 
Alone Coder 

 If a long operation is in progress and
there is nowhere to put an honest percentage meter (on─
for example, this would greatly slow down the work), then
you can write it like this:
 ld a,r
 add a,a
 call showprogress
 Now the percentage meter will be called in
128 times less often.
   Used inZXRarand in graphic
editorScratch.

 * * *

   Reverse byte string (de=beginning,
bc=length):
 ld h,b
 ld l,c
 add hl,de ;hl=end+1
 srl b
 rr c ;bc=wid/2 
mirrorbytes0 
dec hl
 ld a,(de)
ldi
dec hl
ld (hl),a
jp pe,mirrorbytes0

 * * *

   Last time we didn't show how to prepare─
prepare for the cycle djnz:dec c:jr nz:
 ld a,c
dec bc
 inc b
 ld c,b ;bc=1..256 => c=1,
  ;bc=257..512 => c=2...
ld b,a

 * * *

   In addition to the "Etudes" in #11 (there was
definition  AY ) - determination of availability
TurboSound FM: it is important because the player 
under TFM works with register hundred waiting─
tus, which is not on the regularAY.
 Algorithm:
  - turn on FM mode and reading mode
party.
  - write the number from the setting to the 0th register─
bit 7.
  - We are waiting a little.
  - read the register, look at the 7th bit.
   If it's a zero, then it'sTFM,and it's ready. A
if not (that isпрочиталось записанное
число или пустая шина), то это не TFM.
   В Evo SDK это выглядит так:

       LD BC,#FFFD
       LD A,%11111000
       OUT (C),A ;FM on,status rg read on
       ld d,b
       dec d
       jr nz,$-1 ;pause
       XOR A
       OUT (C),A ;select rg 0
       dec d
       jr nz,$-1 ;pause
       LD B,#BF
       OUT (C),B ;write some data in rg 0
       dec d
       jr nz,$-1 ;pause
       inc a ;a=1
       LD B,#FF
       INF ;read status (P=ready)
       JP P,tfmpresent ;a=1
       xor a ;TFM not presenttfmpresent 
ld (TURBOFMON),a

 NB: with the current implementation of whites in
ZX Evocannot work withTFMat frequency 14  
MHz. It is necessary to temporarily enable 3.5 MHz at 
working with ports TFM!Otherwise the sound will deteriorate. 
This cannot be detected in the emulator! 

 * * *

   Our regular readers remember the terms─
addition with a constant for a fixed
time: 
;CY=flag 
;a=initial value 
 jc $+4
 jr $+4
 add a, which is conditionally added 
;a=result 
:19 t 

 Let's supplement it with conditional addition from the register─
strom for a fixed time:;CY=flag 
;a=initial value 
;b=what to conditionally add 
jr c,$+6
nop
 jp $+5
ret nc
 add a,b 
;a=result 
:21 t 

 Or like this: 
;CY=flag 
;c=initial value 
;b=what to conditionally add 
 sbc a,a
 and b
 [add a,c] ;not necessary if initially 0
ld c,a 
;a=result 
:12(16) t 

 * * *

   How to move to the next EGA column
screen on ATM-Turbo (if the screen is connected
at 4000, 8000):
lda,0x9f ;0xa0,if not used
  ;top 8 lines
cp h
 ld bc,0x4000
 adc hl,bc
jp pe,nextstq ;in half the cases
  ;8000->с000 (need 6000)
  ;or a000->e001 (need 4001)
 inc a ;if we use the top 8 lines
 xor h
ld h,a 
nextstq 

 If the screen is connected at 8000, c000, then
the transition can be done like this:
 bit 6,h
 set 6,h
 jr z,$+2+4+2+2+1
 ld a,h
xor 0x60
 ld h,a
 and 0x20
 jr nz,$+3
inc hl

   In some cases it is more profitable for everyone 4
options for the location of the sprite in X sde─
barkсвою  ветку  запоминания  адресов  в
стек, например (в Супер Марио ):

      macro COUNTSCRADDR
       add a,(hl)
       inc h
       ld c,a
       adc a,(hl)
       sub c
       ld l,c
      endm

      macro NEXTCOLUMNS0
       ld h,a ;.00
       push hl
       set 6,h ;.10
       push hl
       xor 0x20
       ld h,a ;.01
       push hl
       set 6,h ;.11
      endm
      macro NEXTCOLUMNS1
       ld c,a
       xor 0x40
       ld h,a ;.10
       push hl
       xor 0x60
       ld h,a ;.01
       push hl
       set 6,h ;.11
       push hl
       ld h,c ;.00
       inc hl
      endm
      macro NEXTCOLUMNS2
       ld h,a
       set 5,h ;.01
       push hl
       set 6,h ;.11
       push hl
       ld h,a ;.00
       inc hl
       push hl
       set 6,h ;.10
      endm
      macro NEXTCOLUMNS3
       ld c,a
       xor 0x60
       ld h,a ;.11
       push hl
       ld h,c ;.00
       inc hl
       push hl
       set 6,h ;.10
       push hl
       ld a,h ;не старое, т.к. был inc hl
       xor 0x60
       ld h,a ;.01
      endm

prcharxy 
;de=gfx 
;la=yx 
;CY=0 
       ld h,tytoscr/256
      rra
      jr c,prcharxy_nextcolumns13
      rra
      jr c,prcharxy_nextcolumns2 
prcharxy_nextcolumns0 
       COUNTSCRADDR
       NEXTCOLUMNS0
       jp prcharxy_scrok 
prcharxy_nextcolumns2 
       COUNTSCRADDR
       NEXTCOLUMNS2
       jp prcharxy_scrok 
prcharxy_nextcolumns13 
      srl a
      jr c,prcharxy_nextcolumns3 
prcharxy_nextcolumns1 
 COUNTSCRADDR
 NEXTCOLUMNS1
jp prcharxy_scrok 
prcharxy_nextcolumns3 
 COUNTSCRADDR
NEXTCOLUMNS3 
prcharxy_scrok 

 3 addresses are remembered, the fourth remains─
in registers. Sprite width in this game
is 8 pixels, but this is not a limitation
method, but a feature of the code of the game itself. B
Evo SDKeach layer of the screen is drawn 
entirely, and 4 addresses are also remembered there.

   In the flooded 3D engine underATM-Turbo 2is─
uses this code:

 ld (hl),a ;0x0000
 set 6,h
 ld (hl),a ;0x4000
 add hl,bc ;bc=0xe000
 ld (hl),a ;0x2000
 set 6,h
 ld (hl),a ;0x6000
add hl,de ;de=0xa001

   To save a register pair, you can
rewrite tosbc hl,bc ... add hl,bc: inc 
hl or on res 6,h:set 5,h ... add hl,bc. 


 * * *

   How to return errors from procedures to─
top? I usually go to the original by mistake
point of the program with stack recovery. But
let us want error handling with correct─
freeing up resources.
   Let the error be returned as  CY=1.
The simplest method in the iS-DOS style:
 
 call proc1
ret c
 
 call proc2
ret c
 
jp proc3
 You can even return the error type
in some register through everything about─
procedures.
   In many practical cases, everything is
held  - just assign─
typeld rp,()andld (),rp,which are not
change flags. Therefore you can write like this:
 
 call proc1
 
 call nc,proc2
ret c
 
jp proc3
 (Used in GIF converter by DIS/
XPJ .) 
 But here, if we want to return the error type─
ki, we'll have to ban the use of this
case in assignments.

 * * *

   How can you do the procedure for capturing re─
resource (for example, opening a file) that
will free it itself at the exit from the block:

openresource 
;if success, autopush closeresource 
;out: nz=error 
...open resource (out: nz=error)... 
       ret nz ;error
       ld hl,closeresource
       ex (sp),hl
       jp (hl)

closeresource 
;out: nz=error 
...close resource (out: nz=error)... 
       ret

   Использование:

work_with_resource 
;out: nz=error 
       call openresource1
       ret nz ;error
       call openresource2
       ret nz ;error 
...work with resources 1 and 2... 
       ret

   Применяется в Nedovigator'е.

                 * * *

   Опять-таки  в  дополнение  к "Этюдам" в 
#11 - ещё  один  способ  вызова функции по 
номеру (номеразаняты не подряд):

;a=команда 
       ld hl,tcmds ;адрес списка команд
       ld bc,ncmds ;число команд в списке
       cpir
       jp nz,ошибка
       add hl,bc
       add hl,bc
       add hl,bc
       ld a,(hl)
       inc hl
       ld h,(hl)
       ld l,a
       jp (hl)

   Список команд:

tcmds 
       db 'a'
       db 's'
       db 'd'
       ...
       db 'x' 
ncmds=$-tcmds 
;дальше в обратном порядке лежат адреса 
;обработчиков: 
       dw PROC_x
       ...
       dw PROC_d
       dw PROC_s
dw PROC_a

   Used inNedoOS - due to support
command numbers fromCP/MandMSX-DOS.The most
Frequent commands are at the beginning of the listCPIR.
When the command numbers are consecutive, CPIR already
not needed, just going through the table is enough.
It's much faster!
   Here is an option from NEO SPECTRUMAN:
 ld h,tab ;7
 ld h,(hl) ;7
jp (hl) ;4
And here fromSuper MariounderNedoOS:
 ;ld l,a
 or 0xc0 ;7
 ld h,a ;4
jp (hl) ;4

Share your thoughts about the article