ZXSRC

Z80 ZX-Spectrum 

Sources Rough Collection

in progress...

Небольшой Блокнотик по программированию

 Z80 на компьютере ZX-SPECTRUM, 

и не только...

Математические операции

ADDITION

;a+hl unsigned slower

;4 bytes and 22 clock cycles

ld b,0 ;ld d,0 ;

ld c,a ;e,a ;

add hl,bc ;add hl,de ;





;a+hl faster unsigned from plutiedev

;5 bytes and 20 clock cycles

;but no other 16-bit register messed up

add a, l    ; A = A+L

ld l, a    ; L = A+L

adc a, h    ; A = A+L+H+carry

sub l       ; A = H+carry

ld h, a    ; H = H+carry





;a+hl fastest addAtoHL:

;5 bytes, 19/20 clock cycles

add a,l

ld l,a

jr nc, $+3

inc h



a+hl ADD A, L

LD L, A

RET NC

INC H

RET 






;a+hl signed from plutiedev

; If A is negative, we need to substract $100 from HL

; (since A's "upper byte" is $FF00)

or a

jp p, Positive

dec h

Positive: ; Then do addition as usual; (to handle the "lower byte")

add a, l

ld l, a

adc a, h

sub l

ld h, a







;(hl)=(hl)+a

add a,(hl)

ld (hl),a







;signed add

ld    e, a    ; DE = A

add   a, a

sbc   a

ld    d, a

add   hl, de  ; HL = HL+DE




ADD 16bits ;andreadrian.de

        LD HL,#1234        ; LOAD DECIMAL VALUE 12345 INTO REGISTER HL

        LD DE,#5           ; LOAD DECIMAL VALUE 7 INTO REGISTER DE

; ADD ROUTINE 16+16BIT=16BIT

; HL = HL + DE

ADD16 ADD HL,DE   ; 16-BIT ADD OF HL AND DE

;RESULT IS IN HL

        RET             ;RESULT IS IN HL





ADD 32bits ;andreadrian.de

        LD      HL,#1213      ; LOAD HEXADECIMAL 1213 TO HL (LOWER 16-BIT)

        LD      DE,#F000      ; LOAD HEXADECIMAL F000 TO DE (LOWER 16-BIT)

        EXX

        LD      HL,#1011      ; LOAD HEXADECIMAL 1011 TO H'L' (UPPER 16-BIT)

        LD      DE,#0          ; LOAD VALUE 0 TO D'E' (UPPER 16-BIT)

        EXX

; H'L'HL = H'L'HL + D'E'DE

ADD32   ADD     HL,DE   ; 16-BIT ADD OF HL AND DE

        EXX

        ADC     HL,DE   ; 16-BIT ADD OF HL AND DE WITH CARRY

        EXX

RET





Kot Ltd,  Кировск, 1996

     Эта процедура приращения регистровой пары HL на величину, заданную в 

регистре А, которая находится в пределах #00 - #FF.

        LD A, приращение           Это быстрее и короче,  

        ADD A,L                    чем:

        LD  L,A                          PUSH  BC

        ADC A,H                          LD  BC, #            

        SUB  L                           ADD HL, BC

        LD H,A                           POP BC 


SUBSTRACTION

;a-hl unsigned from plutiedev

; If A=0 do nothing Otherwise flip A's sign. 

Since the upper byte becomes -1, also

; substract 1 from H.

    neg

    jp    z, Skip

    dec   h

; Now add the low byte as usual

; Two's complement takes care of

; ensuring the result is correct

    add   a, l

    ld    l, a

    adc   a, h

    sub   l

    ld    h, a

Skip:





unsigned hl-de

ld    e, a    ; DE = A

    ld    d, 0

    or    a       ; Clear carry

    sbc   hl, de  ; HL = HL-DE

signed hl-de

ld    e, a    ; DE = A

    add   a, a

    sbc   a

    ld    d, a

    or    a       ; Clear carry

    sbc   hl, de  ; HL = HL-DE





;   Compute (HL)=(DE)-(HL)

SUBHL LD A,E ;compute difference.

SUB L

LD L,A ;store low byte.

LD A,D

SBC A,H

LD H,A ;and then high byte.

RET





;via calc84maniac "Optimized routine for HL=A-HL 

(the negate HL optimization can be derived 

from this by setting A=0 first)

  sub l

  ld l,a

  sbc a,a

  sub h

  ld h,a




;written by calc84maniac

;comment from calc84maniac:

;   To clarify why I did a cpl/scf/adc instead

 of a cpl/inc/add or neg/add,

;   is that it handles the case of A=0 properly. 

Typically, SUB N and

;   ADD A,-N give opposite carry outputs, 

but SUB 0 and ADD A,-0 both reset the

;   carry flag. On the other hand, SCF \ ADC A,255 

will set the carry flag like

;   we want it to.

;NOTE: This is an in-line routine-- there is no RET

  cpl

  scf

  adc a,c

  ld c,a

  jr c,$+3

  dec b

; Here is a callable routine:

  cpl

  scf

  adc a,c

  ld c,a

  ret c

  dec b

  ret


MULTIPLICATION

BC*2,4,8....

Умножить  регистровую пару на 2 (4,8...) можно так:

         AND   A

         RL    C

         RL    B  .


DE*2,4,8....

    sla e

    rl d



А если вам надо перемножить регистр HL,то

это лучше сделать командой сложения ADD :


         ADD   HL,HL      умножить на два

         ADD   HL,HL      на четыре

         ADD   HL,HL      на восемь

         ...........      и т.д.


Например:

HLx32 add hl,hl

HLx16 add hl,hl

HLx8 add hl,hl

HLx4 add hl,hl

HLx2 add hl,hl

ret













;B*2,4,8...

ld b,3        ; Multiply 3 with 4

sla b         ; *4

sla b         ; result: B = 12




;A*2,4,8...

   ld a,15       ; Multiply 15 with 8

    add a,a       ; *8

    add a,a

    add a,a       ; result: A =120




;A*20

    ld a,5       ; Multiply 5 with 20 (= A * 16 + A * 4)

    add a,a       ; *16

    add a,a

    ld b,a       ; Store value of A * 4 in B

    add a,a

    add a,a

    add a,b      ; Add A * 4 to A * 16, result: A = 100




A*15

    ld a,3        ; Multiply 3 with 15 (= A * 16 - A * 1)

    ld b,a        ; Store value of A * 1 in B

    add a,a       ; *16

    add a,a

    add a,a

    add a,a

    sub b        ; result: A = 45






HL = HL*A 

;5.MHLA  - умножение

;INPUT : HL <-- что A <-- на что

;OUTPUT: HL = HL*A;портятся  DE,HL,A

;FASTED bY CREATOR

MHLA    AND     A

        JR      Z,M1H

        EX      DE,HL

        LD      HL,0

M2H     SRL     A

        JP      NC,M3H

        ADD     HL,DE

M3H     SLA     E

        RL      D

        AND     A

        JP      NZ,M2H

        RET 

M1H     LD      H,A

        LD      L,A

        RET 





;a*12=hl a*3=hl a*12=hl (fast)

; Although this specific case 

could be even better as follows:

ld l,a

add a,a ; a*2

add a,l ; a*3

ld h,0

ld l,a ; hl=a*3

add hl,hl ; hl=a*6

add hl,hl ; hl=a*12

; 8 bytes, 45 clocks





;HL*10

Mult10HL

add hl,hl

push de

push hl

pop de

add hl,hl

add hl,hl

add hl,de

pop de





;HL=DE*A

MULT    LD      HL,0

        EX      DE,HL

DUP 8

        RRA 

        JR NC,$+5

        EX DE,HL

        ADD HL,DE

        EX DE,HL

        ADD HL,HL

EDUP 

        EX DE,HL

        RET 






; Multiply 8-bit values H*E ~Grauw

; In:  Multiply H with E

Out: HL = result

Mult8 ld d,0

ld l,d

ld b,8

Mult8_Loop add hl,hl

jr nc,Mult8_NoAdd

add hl,de

Mult8_NoAdd djnz Mult8_Loop

ret




; Multiply 8-bit value with a 16-bit value ~Grauw

; In: Multiply A*DE

Out: HL = result

Mult12:

    ld l,0

    ld b,8

Mult12_Loop:

    add hl,hl

    add a,a

    jr nc,Mult12_NoAdd

    add hl,de

Mult12_NoAdd:

    djnz Mult12_Loop

    ret




; Multiply 16-bit values (with 16-bit result) ~Grauw

; In: Multiply BC*DE

Out: HL = result

Mult16:

    ld a,b

    ld b,16

Mult16_Loop:

    add hl,hl

    sla c

    rla

    jr nc,Mult16_NoAdd

    add hl,de

Mult16_NoAdd:

    djnz Mult16_Loop

    ret




; Multiply 16-bit values (with 32-bit result)

; ~Grauw

; In: BC*DE

; Out: BCHL = result

Mult32:

    ld a,c

    ld c,b

    ld hl,0

    ld b,16

Mult32_Loop:

    add hl,hl

    rla

    rl c

    jr nc,Mult32_NoAdd

    add hl,de

    adc a,0

    jp nc,Mult32_NoAdd

    inc c

Mult32_NoAdd:

    djnz Mult32_Loop

    ld b,c

    ld c,a

    ret





; Multiply 8-bit value with a 16-bit 

value (right rotating) ~Grauw

; In: Multiply A*DE.  Put lowest value 

in A for most efficient calculation

; Out: HL = result

Mult12R:

    ld hl,0

Mult12R_Loop:

    srl a

    jr nc,Mult12R_NoAdd

    add hl,de

Mult12R_NoAdd:

    sla e

    rl d

    or a

    jp nz,Mult12R_Loop

    ret




; Multiply 8-bit value with a 

;16-bit value (unrolled) ~Grauw

; In: Multiply A*DE Out: HL = result

Mult12U:

    ld l,0

    add a,a

    jr nc,Mult12U_NoAdd0

    add hl,de

Mult12U_NoAdd0:

    add hl,hl

    add a,a

    jr nc,Mult12U_NoAdd1

    add hl,de

Mult12U_NoAdd1:

    add hl,hl

    add a,a

    jr nc,Mult12U_NoAdd2

    add hl,de

Mult12U_NoAdd2:

    add hl,hl

    add a,a

    jr nc,Mult12U_NoAdd3

    add hl,de

Mult12U_NoAdd3:

    add hl,hl

    add a,a

    jr nc,Mult12U_NoAdd4

    add hl,de

Mult12U_NoAdd4:

    add hl,hl

    add a,a

    jr nc,Mult12U_NoAdd5

    add hl,de

Mult12U_NoAdd5:

    add hl,hl

    add a,a

    jr nc,Mult12U_NoAdd6

    add hl,de

Mult12U_NoAdd6:

    add hl,hl

    add a,a

    ret nc

    add hl,de

    ret





;HL*6 Умножение HL на шесть :

ADD   HL,HL

LD    B,H

LD    C,L

ADD   HL,HL

ADD   HL,BC




;HL*40 умножение HL на сорок :

ADD   HL,HL

ADD   HL,HL

ADD   HL,HL

LD    B,H

LD    C,L

ADD   HL,HL

ADD   HL,HL

ADD   HL,BC




;by Axor

;HL=HL*128, 35 тактов

LD A,L 

RRC H

RRA

LD H,A

LD L,0

RR L




;MUL:   DE=DE*BC

MUL EX DE,HL

LD DE,0

LD A,16

MUL1 RR B

RR C

JR NC,MUL2

EX DE,HL

ADD HL,DE

EX DE,HL

MUL2 ADD HL,HL

DEC A

JR NZ,MUL1

RET 




Classic 8-bit * 8-bit Unsigned

;Input: 

H = Multiplier, 

E = Multiplicand, 

L = 0, D = 0

;Output: HL = Product

sla h ; optimised 1st iteration

jr nc,$+3

ld l,e

add hl,hl ; unroll 7 times

jr nc,$+3 ; ...

add hl,de ; ...




Classic 16-bit * 8-bit Unsigned

;Input: 

A = Multiplier, 

DE = Multiplicand, 

HL = 0, C = 0

;Output: A:HL = Product

add a,a ; optimised 1st iteration

jr nc,$+4

ld h,d

ld l,e

add hl,hl ; unroll 7 times

rla ; ...

jr nc,$+4 ; ...

add hl,de ; ...

adc a,c ; ...




Classic 16-bit * 16-bit Unsigned

;Input: 

DE = Multiplier, 

BC = Multiplicand, 

HL = 0

;Output: DE:HL = Product

sla e ; optimised 1st iteration

rl d

jr nc,$+4

ld h,b

ld l,c

add hl,hl ; unroll 15 times

rl e ; ...

rl d ; ...

jr nc,$+6 ; ...

add hl,bc ; ...

jr nc,$+3 ; ...

inc de ; ...




;MULTIPLY D*E=DE; 

;0<=result<65535

LD DE,#FEFF

CALL MULT

RET

MULT XOR A

LD B,8

MU1 RRA 

RR E

JR NC,MU0

ADD A,D

MU0 DJNZ MU1

RRA

RR E

LD D,A

RET




    Предлагаю вариант процедуры, 

который имеет большую длину,  

но работает быстрее  при  умножении

больших чисел (B>15) и не  имеет 

синдрома нулевой ошибки. Числа в  

комментариях  означают: 

числитель - длина  (байт),

знаменатель - время (тактов).

LD      L,C;     1/4

LD      D,B;     1/4

LD      H,0;     2/7

LD      E,0;     2/7

LD      B,8;     2/7

; ---------

; 8/29

;-----------------------------

LOOP LD      A,1;     2/7

AND     L;       1/4

JR      Z,PASS;  2/7

ADD     HL,DE;   1/11

PASS RR      H;       2/8

RR      L;       2/8

DJNZ    LOOP; 2/13+8

; ----------

; 12/472

; или (58*8+8)

;------------------------------

RET     ;        1/10

;          ИТОГО:        21/511

    При значении регистра B от 1 до 15 

процедура умножения способом многократного

сложения  (ZX96/4-5, стр.69) выполняется быстрее.




; Процедура знакового умножения D*E=HL

; На входе: DE=множители, BIT 7,D(E) знаковый

; На выходе: HL=результат, BIT 7,H знаковый

ORG #C000

MULTI LD A,D

XOR E

AND #80

EX AF,AF'

RES 7,D

RES 7,E

LD L,D

LD B,8

XOR A

RR L

L1 JR NC,$+3

ADD A,E

RRA

RRL

DJNZ L1

LD H,A

EX AF,AF'

OR H

LD H,A

RET




;from Neos

;chl=chl*b;length is 21 bytes

;maximal time is about 561 tact

_mul24_8 push ix

ex de,hl

xor a

ld l,a

ld h,a

ld xh,8

mul248_ add hl,hl

rla

rl b

jr nc,$+4

add hl,de

adc a,c

dec xh

jr nz,mul248_

ld c,a

pop ix

ret



;from Neos

; de*a=hl

FMULT   ld hl,0

ld b,8

add hl,hl

rlca

jr nc,$+3

add hl,de

djnz FMULT+5

ret




; байт*слово=слово

; Вход  - А-число 1, DE-число 2.

; Выход - HL-результат.


LD HL,0

DUP 8

    ADD HL,HL

  ADD A,A

JR NC,$+3

ADD HL,DE

EDUP


; слово*слово=слово

; Вход  - BC-число 1,DE-число 2.

; Выход - HL-результат.

        

        LD HL,0

LD A,B

DUP 8

   ADD HL,HL

ADD A,A

JR NC,$+3

ADD HL,DE

EDUP

LD A,C

DUP 8

ADD HL,HL

ADD A,A

JR NC,$+3

ADD HL,DE

EDUP


; слово*слово=двойное слово

; Вход  - 

BC-число 1,DE-число 2

; Выход - 

HL-младшая часть слова,

HL'-старшая часть слова.

        LD HL,0

EXX

LD HL,0

LD DE,0

EXX

LD A,B

DUP 8

ADD HL,HL

EXX

ADC HL,HL

EXX

ADD A,A

JR NC,$+6

ADD HL,DE

EXX

ADC HL,DE

EDUP

LD A,C

DUP 8

ADD HL,HL

EXX

ADC HL,HL

EXX

ADD A,A

JR NC,$+6

ADD HL,DE

EXX

ADC HL,DE

EXX

EDUP


; двойное слово*двойное слово=двойное слово

; Вход  - DE' DE-число 1, BC' BC-число 2

; Выход - HL' HL-результат

        LD HL,0

        EXX

        LD HL,0

        EXX

DUP 16

  ADD HL,HL

EXX

ADC HL,HL

EX DE,HL

ADD HL,HL

EX DE,HL

EXX

JR NC,$+6

ADD HL,BC

EXX

ADC HL,BC

EXX

EDUP

DUP 16

ADD HL,HL

EXX

ADC HL,HL

EXX

EX DE,HL

ADD HL,HL

EX DE,HL

JR NC,$+6

ADD HL,BC

EXX

ADC HL,BC

EDUP






;andreadrian.de

        LD      DE,#01213

        LD      BC,#3

        EXX

        LD      DE,#1011

        LD      BC,#0

        EXX

; MULTIPLY ROUTINE 32*32BIT=32BIT

; H'L'HL = B'C'BC * D'E'DE

; NEEDS REGISTER A, CHANGES FLAGS

MUL32

        AND     A        ; RESET CARRY FLAG

        SBC     HL,HL    ; LOWER RESULT = 0

        EXX

        SBC     HL,HL    ; HIGHER RESULT = 0

        LD      A,B      ; MPR IS AC'BC

        LD      B,32     ; INITIALIZE LOOP COUNTER

MUL32LOOP

        SRA     A        ; RIGHT SHIFT MPR

        RR      C

        EXX

        RR      B

        RR      C       ; LOWEST BIT INTO CARRY

        JR      NC,MUL32NOADD

        ADD     HL,DE   ; RESULT += MPD

        EXX

        ADC     HL,DE

        EXX

MUL32NOADD

        SLA     E      ; LEFT SHIFT MPD

        RL      D

        EXX

        RL      E

        RL      D

        DJNZ    MUL32LOOP

        EXX

; RESULT IN H'L'HL

        RET








ПРОЦЕДУРА УМНОЖЕНИЯ HL=B*C.

    (ZX РЕВЮ 96/4-5, стр.69).


    Предлагаю вариант процедуры,

который имеет большую длину,  но

работает быстрее  при  умножении

больших чисел (B>15) и не  имеет

синдрома нулевой ошибки.

    Числа в  комментариях  озна-

чают: числитель - длина  (байт),

знаменатель - время (тактов).


         LD      L,C;     1/4

         LD      D,B;     1/4

         LD      H,0;     2/7

         LD      E,0;     2/7

         LD      B,8;     2/7

;                   ---------

;                         8/29

;-----------------------------


LOOP     LD      A,1;     2/7

         AND     L;       1/4

         JR      Z,PASS;  2/7


         ADD     HL,DE;   1/11

PASS     RR      H;       2/8

         RR      L;       2/8

         DJNZ    LOOP; 2/13+8

;                   ----------

;                        12/472

;                  или (58*8+8)

;------------------------------

         RET     ;        1/10

; ==============================

;          ИТОГО:        21/511


    При значении регистра B от 1

до 15 процедура умножения спосо-

бом многократного  сложения  (ZX

96/4-5, стр.69) выполняется быс-

трее.

    Алгоритм умножения  способом

сложений  со  сдвигами  подробно

описан в книге Р.Токхейм  "Осно-

вы цифровой  электроники",  МИР,

1988г., стр. 238-240.

    Практическое применение про-

цедры  умножения  могут   найти,

например, при расчете адреса на-

чала строки двумерного массива.








   (С) Жильцов Алексей,

         Углич, 1995


   Хочу  предложить  несколько  своих  программ в раздел "Этюды". Это короткие

процедуры вычисления произведения HL=B*C.


Mul_1   LD      L,B

        LD      B,8

        XOR     A

        RR      L

L1      JR      NC,L2

        ADD     A,C

L2      RRA

        RR      L

        DJNZ    L1

        LD      H,A

        RET


Длина 16 байт, максимальная длительность выполнения Тм=328 м.т.


Mul_2   LD      L,B

        LD      B,9

        XOR     A

L1      RR      L

        DEC     B

        RET     Z

        JR      NC,L2

        ADD     A,C

L2      RRA

        LD      H,A

        JR      L1


Длина 15 байт, Тм=430 м.т. Короче предыдущей процедуры на 1 байт, зато медлен-

нее на 25%.


Mul_3   LD      L,0

        LD      H,B

        LD      B,L

        LD      A,8

L1      ADD     HL,HL

        JR      NC,L2

        ADD     HL,BC

L2      DEC     A

        JR      NZ,L1

        RET


Длина  14  байт, Тм=387 м.т. Реализован другой алгоритм по сравнению с Mul_1 и

Mul_2.

   И наконец, самая короткая процедура, занимающая всего 11 байт:


Mul_4   XOR     A

        SBC     HL,HL

        LD      D,A

        OR      B

        RET     Z

        LD      E,C

L1      ADD     HL,DE

        DJNZ    L1

        RET


Длина 11 байт, Тм=6161 м.т.


   Очевидно,  что последнюю процедуру не стоит использовать для активных мате-

матических  вычислений  вследствие  ее  низкого быстродействия. В некоторых же

случаях  (при  гарантии, что  B <= 8 - 10) скорость процедуры сравнима с тремя

другими.  При пониженных требованиях к быстродействию, эта процедура даже пред-

почтительней других по причине своей крайней примитивной простоты.












(С) Васильев Антон

(EARTH SOFTWARE)

HL= B*C

        LD      E,C

        LD      HL,0

        LD      D,0

L1      ADD     HL,DE

        DJNZ    L1

        RET



DIVISION

;деление А на 10 , C - результат      by koshi?

Div10   LD BC,#0AFF

Div     INC C

        SUB B

        RET C

        JP Div




BC/2,4,8....

Деление регистровoй пары на 2, 4, 8 и т.д.

         AND   A

         RR    B

         RR    C


DE/2,4,8

SRL D

RR E



;div A/8,4,2...  ????????????

ld a,153        ; Divide 153 by 8

and a,%11111000 ; Clear bits 0-2 (equals 256 - 8)

rrca            ; /8

rrca

rrca            ; result: a = 19




;hl/32 sam style ;ограничено max: #1fe0/#20

HLdiv32

ADD HL,HL

ADD HL,HL

ADD HL,HL

LD A,H

RET





;HL/A by Andrew Strikes Code

;Вход: HL - делимое, A - делитель

;Выход: HL - частное, A - остаток

DIV_21 NEG

LD C,A

LD B,#11

XOR A

DIV210 ADC A,A

ADD A,C

JR C,DIV211

SUB C

OR A

DIV211 ADC HL,HL

DJNZ DIV210

RET






BC/10 (in BC=-7fff)

bc_div10 ld a, c

sra b

rra

sra b

rra

sra b

rra

sra b

rra

ld c, a

ret








;by b2m

DIV32 ld a,10h ; DE = HLDE/BC, HL = HLDE%BC

DIV321 ex de,hl

add hl,hl

ex de,hl

adс hl,hl

jr c DIV322

push hl

sbс hl,bc

jr nc, DIV323

pop hl

jr DIV325

DIV322 ccf

sbс hl,bc

jr DIV324

DIV323 inc sp

inc sp

DIV324 inc de

DIV325 dec a

jr nz, DIV321

ret




;L=C/B, A=REST

DIVIS   XOR     A

DIVIS2  LD      L,1

D1      RL      C

        RLA 

        CP      B

        JR      C,ZER

        SUB     B

        SLI     L

        JR      NC,D1

        RET 

ZER     SLA     L

        JR      NC,D1

        RET 







;DIV:   BC=DE=DE/HL      

;from Perfect Commander

DIV LD BC,0

LD A,1

DIV1 BIT 7,H

JR NZ,DIV2

ADD HL,HL

INC A

JR DIV1

DIV2 EX DE,HL

DIV4 OR A

SBC HL,DE

CCF 

JR C,DIV3

ADD HL,DE

OR A

DIV RL C

RL B

RR D

RR E

DEC A

JR NZ,DIV4

LD E,C

LD D,B

RET


;DE/BC=DE,HL

;на входе: de-делимое; bc-делитель

;на выходе: de-частное hl-остаток

;by Virtual/bw

divide ld a,b    ;меняем

       cpl       ;

       ld b,a    ;знак

       ld a,c    ;

       cpl       ;у

       ld c,a    ;

       inc bc    ;делителя   t=30

       ld hl,0   ;обнулили новое делимое

       ld a,d    ;сначала двигаем

       rla       ;старший байт делимого ;t=18

       rl l      ;┐

       add hl,bc ;│

       jr c,$+4  ;│8 раз

       sbc hl,bc ;│

       rla       ;┘ t=8*45=360

       ld d,a    ;ст. байт результата

       ld a,e    ;теперь двигаем

       rla       ;младший байт  t=12

       adc hl,hl ;┐

       add hl,bc ;│

       jr c,$+4  ;│8 раз

       sbc hl,bc ;│

       rla       ;┘ t=8*52=416

       ld e,a    ;мл. байт результата ;hl-остаток от деления  ;t=4

;итого: 30+18+360+12+416+4=840 тактов

;если остаток не нужен, 

то конец процедуры должен выглядеть так:

       adc hl,hl ;┐

       add hl,bc ;│

       jr c,$+4  ;│7 раз

       sbc hl,bc ;│

       rla       ;┘ t=7*52=364

       adc hl,hl ;

       add hl,bc ;

       rla       ;

       ld e,a    ;  t=34

;итого: 30+18+360+12+364+34=818 тактов

;p.s.  если  будете  делить на ноль, то,

как  и следовало ожидать, частное будет

;равно  нулю, а остаток - делимому, т.е. 

в этом плане у процедуры глюков нет




; Divide 8-bit values ~Grauw

; In: Divide E by divider C  

Out: A = result, B = rest

Div8:

    xor a

    ld b,8

Div8_Loop:

    rl e

    rla

    sub c

    jr nc,Div8_NoAdd

    add a,c

Div8_NoAdd:

    djnz Div8_Loop

    ld b,a

    ld a,e

    rla

    cpl

    ret




; Divide 16-bit values (with 16-bit result)

;~Grauw

; In: Divide BC by divider DE  

;Out: BC = result, HL = rest

Div16:

    ld hl,0

    ld a,b

    ld b,8

Div16_Loop1:

    rla

    adc hl,hl

    sbc hl,de

    jr nc,Div16_NoAdd1

    add hl,de

Div16_NoAdd1:

    djnz Div16_Loop1

    rla

    cpl

    ld b,a

    ld a,c

    ld c,b

    ld b,8

Div16_Loop2:

    rla

    adc hl,hl

    sbc hl,de

    jr nc,Div16_NoAdd2

    add hl,de

Div16_NoAdd2:

    djnz Div16_Loop2

    rla

    cpl

    ld b,c

    ld c,a

    ret

;Thanks to Flyguille for the Div16 routine, 

taken from his MNBIOS source code.




;Classic 8-bit / 8-bit Unsigned

;Input: D = Dividend, E = Divisor, A = 0

;Output: D = Quotient, A = Remainder

sla d ; unroll 8 times

rla ; ...

cp e ; ...

jr c,$+4 ; ...

sub e ; ...

inc d ; ...




;Input: D = Dividend, E = Divisor, A = 0, Carry = 0

;Output: A = Quotient, E = Remainder

rl d ; unroll 8 times

rla ; ...

sub e ; ...

jr nc,$+3 ; ...

add a,e ; ...

ld e,a ; save remainder

ld a,d ; complement the result

cpl




Classic 16-bit / 8-bit Unsigned

;Input: HL = Dividend, C = Divisor, A = 0

;Output: HL = Quotient, A = Remainder (see note)

add hl,hl ; unroll 16 times

rla ; ...

cp c ; ...

jr c,$+4 ; ...

sub c ; ...

inc l ; ...




Classic 16-bit / 16-bit Unsigned

;Input: A:C = Dividend, DE = Divisor, HL = 0

;Output: A:C = Quotient, HL = Remainder

slia c ; unroll 16 times

rla ; ...

adc hl,hl ; ...

sbc hl,de ; ...

jr nc,$+4 ; ...

add hl,de ; ...

dec c ; ...




Classic 24-bit / 8-bit Unsigned

;Input: E:HL = Dividend, D = Divisor, A = 0

;Output: E:HL = Quotient, A = Remainder

add hl,hl ; unroll 24 times

rl e ; ...

rla ; ...

cp d ; ...

jr c,$+4 ; ...

sub d ; ...

inc l ; ...




Classic 24-bit / 16-bit Unsigned

;Input: A:BC = Dividend, DE = Divisor, HL = 0

;Output: A:BC = Quotient, HL = Remainder

slia c ; unroll 24 times

rl b ; ...

rla ; ...

adc hl,hl ; ...

sbc hl,de ; ...

jr nc,$+4 ; ...

add hl,de ; ...

dec c ; ...




Classic 32-bit / 8-bit Unsigned

;Input: DE:HL = Dividend, C = Divisor, A = 0

;Output: DE:HL = Quotient, A = Remainder

add hl,hl ; unroll 32 times

rl e ; ...

rl d ; ...

rla ; ...

cp c ; ...

jr c,$+4 ; ...

sub c ; ...

inc l ; ...




В  DE - делимое, в  C - делитель,

результат в HL, остаток в A.

;DIVIDE DE/C=HL

LD DE,768

LD C,3

CALL DIV

RET

DIV LD B,16

LD HL,0

LD A,H

DV1 RL E

RL D

RLA

SUB C

JR NC,DV2

ADD A,C

DV2 CCF 

RL L

RL H

DJNZ DV1

RET




Деление регистровoй пары 

на /2, /4, /8 и т.д. 

AND   A

RR    B

RR    C

При повторе данного примера N раз 

вы разделите число на 2 в степени N. 

Таким  методом возможно разделить и 

отдельные  регистры.




;A/6

деление A на шесть /6:

LD    A,делимое

LD    B,#00

LABEL SUB   #06 (делитель)

RET   C

INC   B

JR    LABEL 

на выходе этой  процедуры  частное  

будет находится в регистре B  

(не  забудте, что таким методом можно

выполнить только  целочисленное деление!).




;BC/DE Divides BC by DE. 

Gives result in BC, remainder in HL by hll

udiv16 ld a,d

or e

ret z ;Return NC if dividing by 0

ld hl,0

ld a,16

udiv1 scf

rl c

rl b

adc hl,hl

sbc hl,de

jr nc,udiv2

add hl,de

dec c

udiv2 dec a

jr nz,udiv1

scf

ret




C_Div_D:

;Inputs:     

C is the numerator     

D is the denominator

;Outputs:

A is the remainder

B is 0

C is the result of C/D

D,E,H,L are not changed

  ld b,8

  xor a

C_Div_D_loop:

  sla c

  rla

  cp d

  jr c,+_

  inc c

  sub d

_:

  djnz C_Div_D_loop

  ret




BC_Div_DE:   

BC/DE ==> BC, remainder in HL

;min: 1166cc     

;max: 1310cc      

;avg: 1238cc

;22 bytes

  ld hl,0

  ld a,b

  ld b,16

div_loop:

  ;shift the bits from BC into HL

  sla c \ rla

  adc hl,hl

  sbc hl,de

  jr nc,div_inc_acc

  add hl,de

  .db $FE     ;this begins the instruction 

;'cp *', so it eats the next byte.


div_inc_acc:

  inc c

div_loop_done:

  djnz div_loop

  ld b,a

  ret




;from action by vav

Вход:de-число1 (2 байта)/bc-65536-число2 (2 байта)            

Выход:a-остаток от деления:de=число1/число2                    

DELENIE ld      hl,0                      

        ld      a,e                       

        add     a,a                       

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA57                  

        sbc     hl,bc                     

NABA57  rla                               

        rl      d                         

NABA58  adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA60                  

NABA59  sbc     hl,bc                     

NABA60  rla                               

        rl      d                         

NABA61  adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA62                  

        sbc     hl,bc                     

NABA62  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA63                  

        sbc     hl,bc                     

NABA63  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA64                  

        sbc     hl,bc                     

NABA64  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA65                  

        sbc     hl,bc                     

NABA65  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA66                  

        sbc     hl,bc                     

NABA66  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA67                  

        sbc     hl,bc                     

NABA67  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA68                  

        sbc     hl,bc                     

NABA68  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA69                  

        sbc     hl,bc                     

NABA69  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA70                  

        sbc     hl,bc                     

NABA70  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA71                  

        sbc     hl,bc                     

NABA71  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA72                  

        sbc     hl,bc                     

NABA72  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA73                  

        sbc     hl,bc                     

NABA73  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA74                  

        sbc     hl,bc                     

NABA74  rla                               

        rl      d                         

        adc     hl,hl                     

        add     hl,bc                     

        jr      c,NABA75                  

        sbc     hl,bc                     

NABA75  rla                               

        rl      d                         

        ld      e,a                       

        ret                       




;from Neos

;chl=chl/d ;a-remainder ;length is 22 bytes

;maximal time is about 1200 tacts

_div24_8 xor a

sub d

ld d,a

xor a

ld b,24

div248_ adc hl,hl

rl c

rla

add a,d

jr c,$+3

sub d

djnz div248_

adc hl,hl

rl c

ret




;from Neos

; hl/d=a hl\d=l

FDIV xor a

ld e,a

ld b,8

fdiv1 rr d

rr e

sbc hl,de

jr nc,$+3

add hl,de

rla

djnz fdiv1

cpl

ret




;bc/de=bc,hl

;Divides BC by DE. 

;Gives result in BC, remainder in HL.

udiv16  ld      a,d    

        or      e

        ret     z       ;Return NC if dividing by 0

        ld      hl,0

        ld      a,16

udiv1   scf

        rl      c

        rl      b

        adc     hl,hl

        sbc     hl,de

        jr      nc,udiv2

        add     hl,de

        dec     c

udiv2   dec     a

        jr      nz,udiv1

        scf

        ret




; A,HL=(A,HL)/(B,DE)

DIVISIO  LD      C,A

        XOR     A

        EXX 

        LD      HL,1

        LD      B,H

        EXX 

DIV1    SLA     L

        RL      H

        RL      C

        RLA 

        CP      B

        JR      C,DIV2

        JR      NZ,DIV3

        EX      AF,AF'

        LD      A,C

        CP      D

        JR      C,DIV22

        JR      NZ,DIV32

        LD      A,H

        CP      E

        JR      C,DIV22

DIV32   EX      AF,AF'

DIV3    EX      AF,AF'

        LD      A,L

        LD      L,H

        LD      H,C

        AND     A

        SBC     HL,DE

        LD      C,H

        LD      H,L

        LD      L,A

        JR      NC,DIV33

        EX      AF,AF'

        SUB     B

        DEC     A

DIV34   EXX 

        SLI     L

        RL      H

        RL      B

        EXX 

        JP      NC,DIV1

        JP      DIVEXIT

DIV22   EX      AF,AF'

DIV2    EXX 

        SLA     L

        RL      H

        RL      B

        EXX 

        JP      NC,DIV1

DIVEXIT EXX 

        PUSH    HL

        LD      A,B

        EXX 

        POP     HL

        RET 

DIV33   EX      AF,AF'

        SUB     B

        JP      DIV34






;HL = HL/DE

;1.DIV - деление

;INPUT : HL <-- что   DE <-- на что

;OUTPUT: HL = HL/DE ;портятся  DE,HL,A

DIV     LD      A,D

        OR      E

        RET     Z

        PUSH    DE

        PUSH    BC

        LD      A,1

DIV_0   PUSH    HL

        SBC     HL,DE

        JP      C,HL0

        SBC     HL,DE

        JP      C,DIV_1

DIV_01  INC     A

        SLA     E

        RL      D

        POP     HL

        JP      DIV_0

DIV_1   POP     HL

        LD      BC,0

DIV_2   AND     A

        JP      NZ,DIV_3

        LD      H,B

        LD      L,C

        POP     BC

        POP     DE

        RET 

DIV_3   SBC     HL,DE

        JP      NC,DIV_4

        ADD     HL,DE

DIV_4   CCF 

        RL      C

        RL      B

        SRL     D

        RR      E

        DEC     A

        JP      DIV_2

HL0     CP      1

        JP      NZ,DIV_01

        POP     HL

        POP     BC

        POP     DE

        LD      HL,0

        RET 


SIN, COS

;by SerzhSoft;

;вычисление синуса HL

;A=sin(HL)*255, CF=sign (NC=[+], C=[-])

SIN_HL

        dec     h

        jr      z,GOSIN2

        inc     h

        jr      nz,SIN360

        ld      h,SINTAB/256

        ld      a,l

        cp      180

        jr      nc,GOSIN1

        ld      a,(hl)

        and     a

        ret

GOSIN1  sub     180

        ld      l,a

        ld      a,(hl)

        scf

        ret

GOSIN2  ld      a,l

        cp      104

        jr      nc,GOSIN3

        ld      l,a

        ld      a,104

        sub     l

        ld      l,a

        ld      h,SINTAB/256

        ld      a,(hl)

        scf

        ret

GOSIN3  inc     h

SIN360  ld      bc,360

        and     a

LPSIN1  sbc     hl,bc

        jr      nc,LPSIN1

        add     hl,bc

        jp      SIN_HL

;-------

;вычисление косинуса HL

;A=cos(HL)*255, CF=sign (NC=[+], C=[-])

COS_HL

        dec     h

        jr      z,GOCOS3

        inc     h

        jr      nz,COS360

        ld      h,SINTAB/256

        ld      a,l

        cp      180

        jr      nc,GOCOS2

        sub     90

        jr      c,GOCOS1

        ld      l,a

        ld      a,(hl)

        scf

        ret

GOCOS1  add     a,180

        ld      l,a

        ld      a,(hl)

        and     a

        ret

GOCOS2  sub     90

        ld      l,a

        ld      a,(hl)

        scf

        ret

GOCOS3  ld      a,l

        cp      14

        jr      nc,GOCOS4

        add     a,166

        ld      l,a

        ld      h,SINTAB/256

        ld      a,(hl)

        scf

        ret

GOCOS4  cp      104

        jr      nc,GOCOS5

        sub     14

        ld      l,a

        ld      h,SINTAB/256

        ld      a,(hl)

        and     a

        ret

GOCOS5  inc     h

COS360  ld      bc,360

        and     a

LPCOS1  sbc     hl,bc

        jr      nc,LPCOS1

        add     hl,bc

        jp      COS_HL

;выравнивание на сегмент (граница 256)

        ds      $/256*256+256-$

SINTAB  ;insert  "sincos.d"

db #0D,#12,#16,#1B,#1F,#23,#28,#2C,#31,#35,#39,#3E,#42,#46,#4A,#4F

db #53,#57,#5B,#5F,#64,#68,#6C,#70,#74,#78,#7C,#7F,#83,#87,#8B,#8E

db #92,#96,#99,#9D,#A0,#A4,#A7,#AB,#AE,#B1,#B4,#B7,#BA,#BD,#C0,#C3

db #C6,#C9,#CC,#CE,#D1,#D3,#D6,#D8,#DA,#DD,#DF,#E1,#E3,#E5,#E7,#E9

db #EB,#EC,#EE,#F0,#F1,#F2,#F4,#F5,#F6,#F7,#F8,#F9,#FA,#FB,#FC,#FC

db #FD,#FE,#FE,#FE,#FF,#FF,#FF,#FF,#FF,#FF,#FF,#FE,#FE,#FE,#FD,#FC

db #FC,#FB,#FA,#F9,#F8,#F7,#F6,#F5,#F4,#F2,#F1,#F0,#EE,#EC,#EB,#E9

db #E7,#E5,#E3,#E1,#DF,#DD,#DA,#D8,#D6,#D3,#D1,#CE,#CC,#C9,#C6,#C3

db #C0,#BD,#BA,#B7,#B4,#B1,#AE,#AB,#A7,#A4,#A0,#9D,#99,#96,#92,#8E

db #8B,#87,#83,#7F,#7C,#78,#74,#70,#6C,#68,#64,#5F,#5B,#57,#53,#4F

db #4A,#46,#42,#3E,#39,#35,#31,#2C,#28,#23,#1F,#1B,#16,#12,#0D,#09

db #04






;--------------------------------------;

;     SIN-TABLE MAKING ALGORYTHMS      ;

; conception & code by Kolotov Sergey  ;

; (c) SerzhSoft, Shadrinsk, apr, 1998  ;

;--------------------------------------;

        ORG     #8000

;--------------------------------------;

STACK_A EQU     #2D28

FP_TO_A EQU     #2DD5

;

SIN_MAX EQU     #7F

SIN_ADD EQU     0

;

SINTBL1 EQU     #6000

SINTBL2 EQU     #6100

;--------------------------------------;

MAINPRG

        LD      C,SIN_MAX

        LD      B,SIN_ADD

        LD      DE,SINTBL1

        PUSH    BC

        CALL    SINCALC

        POP     BC

        LD      DE,SINTBL2

        CALL    SINMAKE

        RET

;--------------------------------------;

SINCALC

        PUSH    DE

        PUSH    BC

        PUSH    DE

        LD      A,C

        CALL    STACK_A

        POP     DE

        LD      A,E

        CALL    STACK_A

        RST     #28     ;calc

        DB      #A3     ;stk_pi/2

        DB      #34,#40,#B0,#00,#40 ;#40

        DB      #05     ;divide

        DB      #04     ;multiply

        DB      #1F     ;sin

        DB      #04     ;multiply

        DB      #38     ;endcalc

        CALL    FP_TO_A

        JR      Z,$+4   ;->

        NEG             ; |

        POP     BC      ;<-

        ADD     A,B

        POP     DE

        LD      (DE),A

        INC     E

        JR      NZ,SINCALC

        RET

;--------------------------------------;

SINMAKE

        INC     C

        LD      HL,SIN_DAT

        PUSH    BC

        LD      B,E

LP_SMK1 PUSH    HL

        LD      H,(HL)

        LD      L,B

        LD      A,#08

LP_SMK2 ADD     HL,HL

        JR      NC,$+3

        ADD     HL,BC

        DEC     A

        JR      NZ,LP_SMK2

        LD      A,H

        LD      (DE),A

        POP     HL

        INC     HL

        INC     E

        BIT     6,E

        JR      Z,LP_SMK1

        LD      H,D

        LD      L,E

        DEC     L

        LD      A,(HL)

        LD      (DE),A

        INC     E

LP_SMK3 LD      A,(HL)

        LD      (DE),A

        INC     E

        DEC     L

        JR      NZ,LP_SMK3

LP_SMK4 LD      A,(HL)

        NEG

        LD      (DE),A

        INC     L

        INC     E

        JR      NZ,LP_SMK4

        POP     BC

LP_SMK5 LD      A,(DE)

        ADD     A,B

        LD      (DE),A

        INC     E

        JR      NZ,LP_SMK5

        RET

;-------;

SIN_DAT

  DB  #00,#06,#0D,#13,#19,#1F,#25,#2C

  DB  #32,#38,#3E,#44,#4A,#50,#56,#5C

  DB  #62,#67,#6D,#73,#78,#7E,#83,#88

  DB  #8E,#93,#98,#9D,#A2,#A7,#AB,#B0

  DB  #B4,#B9,#BD,#C1,#C5,#C9,#CD,#D0

  DB  #D4,#D7,#DB,#DE,#E1,#E4,#E7,#E9

  DB  #EC,#EE,#F0,#F2,#F4,#F6,#F7,#F9

  DB  #FA,#FB,#FC,#FD,#FE,#FE,#FF,#FF

;--------------------------------------;






; sincos.z80

; CORDIC for sin() and cos() for the Zilog Z80 and Assembler Z80DT

; Author: Andre Adrian

; Version: 12apr2011

; length is 341 bytes


; LOAD TEST VALUES

    LD      DE,12AFH                ; Angle = -1.57079632679 (-90°)

    LD      BC,9B78H

    CALL    SINCOS

; expected sin = B'C'BC = 0C0000000H (-1.0)

; expected cos = D'E'DE = 000000000H (0.0)

; delivered sin = B'C'BC = 0BFFFFFFFH

; delivered cos = D'E'DE = 000000031H

    HALT


;==================================================

; Constants

;

CN:        EQU        22

CM:        EQU        8


;==================================================

; variable storage (RAM)

;

Angle:

    DEFW    0,0

Y:

    DEFW    0,0

X:

    DEFW    0,0

Yold:

    DEFW    0,0

pATR:

    DEFW    0

Step:

    DEFB    0


;==================================================

; constant storage (ROM)

;

ATR:

    DEFW 0F6A8H,03243H      ; atan(1)

    DEFW 06705H,01DACH      ; atan(1/2)

    DEFW 0BAFCH,00FADH      ; atan(1/4)

    DEFW 06EA6H,007F5H      ; atan(1/8)

    DEFW 0AB76H,003FEH      ; ...

    DEFW 0D55BH,001FFH

    DEFW 0FAAAH,000FFH

    DEFW 0FF55H,0007FH

    DEFW 0FFEAH,0003FH


;==================================================

; N BIT ARITHMETRIC SHIFT RIGHT ROUTINE 32BIT = 32BIT

; BCDE >>= A

; CHANGES FLAGS

;

SRBCDE:

    SUB     8               ; SET CARRY FLAG IF A < 8

    JR      C,SRBEBT        ; NO MORE BYTES TO SHIFT

    LD      E,D             ; SHIFT BITS 8..15 TO 7..0

    LD      D,C             ; SHIFT BITS 16..23 TO 8..15

    LD      C,B             ; SHIFT BITS 24..31 TO 16..23

    LD      B,0             ; ASSUME POSITIVE NUMBER

    BIT     7,C             ; SET ZERO FLAG IF BIT 7 == 0

    JR      Z,SRBEPS

    DEC     B                   ; CHANGE + TO - SIGN

SRBEPS:

    JR      SRBCDE

SRBEBT:

    ADD     A,8             ; UNDO SUB 8, SET ZERO FLAG IF A == 0

SRBELP:

    JR      Z,SRBERT        ; NO MORE BITS TO SHIFT

    SRA     B

    RR      C

    RR      D

    RR      E

    DEC     A               ; SET ZERO FLAG IF A == 0

    JR      SRBELP

SRBERT:

    RET                                                ; RESULT IS IN BCDE.


;==================================================

; SIN() COS() ROUTINE 2 * 32BIT = 32BIT

; B'C'BC, D'E'DE = f(BCDE)

; NEEDS REGISTERS A BC DE HL, CHANGES FLAGS

;

SINCOS:

    LD      (Angle),DE      ; store argument

    LD      (Angle+2),BC

    SUB     A               ; A = 0

    LD      (Step),A        ; i = 0

    LD      L,A

    LD      H,A

    LD      (Y),HL          ; y = 0;

    LD      (Y+2),HL

    LD      HL,03B6AH       ; x = FIXED(0.607252935..);

    LD      (X),HL

    LD      HL,026DDH

    LD      (X+2),HL

    LD      HL,ATR          ; pATR = ATR;

    LD      (pATR),HL

SNCSDO:                         ; do {

    LD      HL,(Y)          ;   yold = y;

    LD      (Yold),HL

    LD      HL,(Y+2)

    LD      (Yold+2),HL

                            ;   y += (angle >= 0)? x >> i: -(x >> i);

    LD      DE,(X)          ;   x >> i

    LD      BC,(X+2)

    LD      A,(Step)

    CALL    SRBCDE

    LD      A,(Angle+3)     ;   (angle >= 0)?

    RLA                     ;   Bit 7 to Carry

    LD      HL,(Y)

    JR      C,SNCSL1

    ADD     HL,DE           ;   y += x >> i

    LD      (Y),HL

    LD      HL,(Y+2)

    ADC     HL,BC

    JR      SNCSE1

SNCSL1:

    AND     A               ;   y -= x >> i

    SBC     HL,DE

    LD      (Y),HL

    LD      HL,(Y+2)

    SBC     HL,BC

SNCSE1:

    LD      (Y+2),HL

                            ;   x -= (angle >= 0)? yold >> i: -(yold >> i);

    LD      DE,(Yold)       ;   yold >> i

    LD      BC,(Yold+2)

    LD      A,(Step)

    CALL    SRBCDE

    LD      A,(Angle+3)     ;   (angle >= 0)?

    RLA

    LD      HL,(X)

    JR      C,SNCSL2

    AND     A               ;   x -= yold >> i

    SBC     HL,DE

    LD      (X),HL

    LD      HL,(X+2)

    SBC     HL,BC

    JR      SNCSE2

SNCSL2:

    ADD     HL,DE           ;   x += yold >> i

    LD      (X),HL

    LD      HL,(X+2)

    ADC     HL,BC

SNCSE2:

    LD      (X+2),HL

                           ;   a = (i < M+1)? *pATR++: atr_fixed[M] >> (i-M);

    LD      A,(Step)       ;   (i < M+1)?

    SUB     CM+1           ;   i-(M+1)

    JR      NC,SNCSL3

    LD      HL,(pATR)      ;   *pATR++

    LD      E,(HL)

    INC     HL

    LD      D,(HL)

    INC     HL

    LD      C,(HL)

    INC     HL

    LD      B,(HL)

    INC     HL

    LD      (pATR),HL

    JR      SNCSE3

SNCSL3:

    LD      DE,(ATR+32)     ;   atr[M] >> i-M

    LD      BC,(ATR+34)

    INC     A               ;   i-M

    CALL    SRBCDE

SNCSE3:

                            ;   angle -= (angle >= 0)? a: -a;

    LD      A,(Angle+3)     ;   (angle >= 0)?

    RLA

    LD      HL,(Angle)

    JR      C,SNCSL4

    AND     A               ;   angle -= a

    SBC     HL,DE

    LD      (Angle),HL

    LD      HL,(Angle+2)

    SBC     HL,BC

    JR      SNCSE4

SNCSL4:

    ADD     HL,DE           ;   angle += a

    LD      (Angle),HL

    LD      HL,(Angle+2)

    ADC     HL,BC

SNCSE4:

    LD      (Angle+2),HL

                            ; } while (++i <= N-1);

    LD      HL,Step         ; ++i

    INC     (HL)

    LD      A,CN-1          ; N-1 >= ++i

    CP      (HL)

    JP      NC,SNCSDO

                            ; RESULT IS IN MEM(X) AND MEM(Y)

    LD      DE,(X)

    LD      BC,(Y)

    EXX

    LD      DE,(X+2)

    LD      BC,(Y+2)

    EXX

    RET                     ; RESULT IS IN D'E'DE AND B'C'BC





SerzhSoft

          КАК СИHУС ЗАБАБАХАТЬ 

                    Лучше в pуках синица,

                   чем под кpоватью утка!

 

     Hастало  вpемя поговоpить о наболев-

шем,  а  конкpетнее  -  о синусах. Дело в

том,  что  во  многих мегадемных эффектах

используются  именно  синусные  таблички.

Это  может  быть  пpостенькая плазма, ка-

кое-нибудь  тело,  движущееся  по плавной

тpаектоpии,  полет над гоpами, тоннельчик

и  куча  еще  всего  дpугого, чего только

можно пожелать...

     Тpадиционно, да и удобно, of course,

создавать  синусную  табличку с pазмеpом,

кpатным  256  байт,  а  еще лучше - pовно

#0100!  : -) Пpи этом табличка должна на-

чинаться  с  адpеса, выpавненного на сег-

мент,  то  есть младший байт этого адpеса

pавен нолику (#?? 00).

     Создать пpостейшую sin-table можно с

помощью обыкновенного бейсика. К пpимеpу,

нам  необходима табличка, pасположенная с

адpеса  24576  (#6000),  длиной 256 байт,

амплитудой  (pазмахом)  синуса от -127 до

+127 и pазмеpом в один пеpиод. Получаем:

 

 10 FOR i=0 TO 255

 20 POKE 24576+i, SIN (PI/128*i)*127

 30 NEXT i

 

     Тепеpь сгенеpиpованную табличку мож-

но сохpанить на диске и встpаивать в свои

ассемблеpные  эффекты по INSERT, INCBIN и

т. д.

     Hо  вот  вы  pешили немного изменить

амплитуду,  скажем от -63 до +63, или вам

вдpуг  стукнуло в голову, что "сеpединной

точкой" должна быть не 0, а, скажем, 128.

То есть получить pазбpос значений таблич-

ки  от  128-63  до 128+63. Конечно, можно

снова "загpузить" BASIC и набpать немного

дpугую пpогpамму:

 

 10 FOR i=0 TO 255

 20 POKE 24576+i, SIN (PI/128*i)*63+128

 30 NEXT i

 

     Затем  опять сохpанить все на диске,

и  пpодолжать  кодить в асм'е. Hо, навеp-

ное,  даже  последнему лaмеpу стало ясно,

что  тут что-то не так. Hеэффективно, по-

теpя  вpемени, да еще место на диске вся-

кие  "левые" файлы занимают. Плюс, к тому

же, тоpмознутость компиляции пpи подгpуз-

ке по INCBIN'у. Да и хpанить целый синус-

ный пеpиод в памяти, когда можно обойтись

и четвеpью... Hет, с этим надо что-то де-

лать!

     И  выход, конечно же, есть. Hе уж то

нам  слабо  накодить  пpоцедуpу ГЕHЕPАЦИИ

синуса  на  ассемблеpе.  Да  pаз плюнуть!

Хлоп, и уже готова подпpогpамма с исполь-

зованием ПЗУ 'шного калькулятоpа. Похожая

вещь   пpисутствовала  в  Deja  Vu  #03 ,

статья  "Плавающие  атpибуты" .  Здесь же

пpиводится немного улучшенная пpоцедуpа:

 

STACK_A EQU     #2D28

FP_TO_A EQU     #2DD5

;

SINCALC

        PUSH    DE

        PUSH    BC

        PUSH    DE

        LD      A,C

        CALL    STACK_A

        POP     DE

        LD      A,E

        CALL    STACK_A

        RST     #28     ;calc

        DB      #A3     ;stk_pi/2

        DB      #34,#40,#B0,#00,#40 ;#40

        DB      #05     ;divide

        DB      #04     ;multiply

        DB      #1F     ;sin

        DB      #04     ;multiply

        DB      #38     ;endcalc

        CALL    FP_TO_A

        JR      Z,$+4   ;->

        NEG             ; |

        POP     BC      ;<-

        ADD     A,B

        POP     DE

        LD      (DE),A

        INC     E

        JR      NZ,SINCALC

        RET

 

     Hа входе в pегистpе C мы должны ука-

зать  "pазмах"  синуса  (амплитуда: -С...

+С),  в pегистpе B - центpальное значение

(сколько  пpибавлять), а pегистpовая паpа

DE  должна  содеpжать  адpес  буфеpа  под

табличку.  Пpичем, этот адpес обязательно

должен  быть кpатен 256 (выpавнен на сег-

мент).  И  вот, пpимеpно чеpез 15 секунд,

мы получим те заветные 256 байт!

     "Так до-о-олго!!!" - спpаведливо за-

метите Вы и будете пpавы. Hу а что вы еще

хотели  всего за 39 байт памяти, занимае-

мых  пpоцедуpой.  Hо,  конечно же, данный

пpимеp  был  пpиведен  лишь  для  полноты

каpтины,  а настоящую пpоцедуpу мы pазбе-

pем  именно  сейчас. Ведь не будете же вы

в  демке  писать  "Please, wait 15 sec...

Decrunching..." только pади какой-то таб-

лички  в 256 байт! И пpавильно! Итак, вот

скоpостной  аналог вышепpиведенной пpоце-

дуpы, pаботающий несpавненно быстpее, хо-

тя и занимающий уже 55 байт + 64 байта на

данные. Итого: 119 байт. Hо скоpость!

 

SINMAKE

        INC     C

        LD      HL,SIN_DAT

        PUSH    BC

        LD      B,E

LP_SMK1 PUSH    HL

        LD      H,(HL)

        LD      L,B

        LD      A,#08

LP_SMK2 ADD     HL,HL

        JR      NC,$+3

        ADD     HL,BC

        DEC     A

        JR      NZ,LP_SMK2

        LD      A,H

        LD      (DE),A

        POP     HL

        INC     HL

        INC     E

        BIT     6,E

        JR      Z,LP_SMK1

        LD      H,D

        LD      L,E

        DEC     L

        LD      A,(HL)

        LD      (DE),A

        INC     E

LP_SMK3 LD      A,(HL)

        LD      (DE),A

        INC     E

        DEC     L

        JR      NZ,LP_SMK3

LP_SMK4 LD      A,(HL)

        NEG

        LD      (DE),A

        INC     L

        INC     E

        JR      NZ,LP_SMK4

        POP     BC

LP_SMK5 LD      A,(DE)

        ADD     A,B

        LD      (DE),A

        INC     E

        JR      NZ,LP_SMK5

        RET

;

SIN_DAT

  DB  #00,#06,#0D,#13,#19,#1F,#25,#2C

  DB  #32,#38,#3E,#44,#4A,#50,#56,#5C

  DB  #62,#67,#6D,#73,#78,#7E,#83,#88

  DB  #8E,#93,#98,#9D,#A2,#A7,#AB,#B0

  DB  #B4,#B9,#BD,#C1,#C5,#C9,#CD,#D0

  DB  #D4,#D7,#DB,#DE,#E1,#E4,#E7,#E9

  DB  #EC,#EE,#F0,#F2,#F4,#F6,#F7,#F9

  DB  #FA,#FB,#FC,#FD,#FE,#FE,#FF,#FF

 

     Тепеpь  пpи  инсталляции какого-либо

вашего  FX'а достаточно вызвать SINMAKE с

нужными  паpаметpами,  и  спустя  секунду

табличка  уже готова! Можно генеpить нес-

колько  pазных  табличек...  Или одну, но

состоящую  из pазных синусных пеpиодов, с

pазными  амплитудами...  А затем, скажем,

пустить  по этой таблице scroller и отpы-

ваться на всю катушку...

     В пpиложении вы найдете ассемблеpный

исходник  с  двумя  этими пpоцедуpами.  И

помните:

 

         Лучше в пpогpамме синус,

         чем в мегадеме глюки!!!

 

     P.S.  А  ведь  можно  обойтись и без

таблички данных SIN_DAT! Кто догадается -

как? : -)


SQR

;извлечение    квадратного    корня   

;из регистровой    пары.  

;на входе HL-число из которого 

;необходимо извлечь квадратный корень

;на выходе A-квадратный корень из HL

;by Virtual/BW

sqrhl xor a     ;обнулили a;)

 ld de,64  ;этого требует алгоритм ;  t=14

sla h     ;берем два самых левых

adc a,a   ;бита аргумента

sla h     ;

adc a,a   ;

jr z,$+4  ;если они не равны

dec a     ;нулю, то увеличиваем

inc d     ;результат  ;   t=39

sla h     ;┐далее следуем

adc a,a   ;│алгоритму извлечения

sla h     ;│квадратного корня

adc a,a   ;│"столбиком", только

sla d     ;│

ld c,d    ;│3 раза

sli c     ;│

cp c      ;│

jr c,$+4  ;│

sub c     ;│

inc d     ;┘  t=63*3=189

sla l     ;┐проделываем похожую

adc a,a   ;│операцию с

sla l     ;│младшим байтом

adc a,a   ;│аргумента

sla d     ;│

ld c,d    ;│2 раза

sli c     ;│

cp c      ;│

jr c,$+4  ;│

sub c     ;│

inc d     ;┘  t=63*2=126

ld h,a    ;

or a      ;обнулили флаг переноса ;  t=8

sbc hl,de ;теперь нам не хватает

jr nc,$+3 ;одного регистра d для

add hl,de ;выполнения последних

ccf       ;двух циклов, придется

rl d      ;использовать более

add hl,hl ;медленные операции

add hl,hl ;с регистровыми парами  ;  t=67

sbc hl,de ;и последний раз

ccf       ;выполняем расчеты, не

ld a,d    ;восстанавливая hl

adc a,a   ;в a-результат ;  t=27

;итого: 14+39+189+126+8+67+27=470 тактов





;извлечение    квадратного    корня   

;из трехбайтового числа.

;На входе: dhl=d*65536+hl - само число

;На выходе: hl - тот самый квадратный корень

;by Virtual/BW

sqrdhl xor a    ;начало процедуры

ld bc,64 ;практически аналогично ;предыдущей  ;  t=14

sla d    ;

adc a,a  ;

sla d    ;

adc a,a  ;  t=39

jr z,$+4 ;

dec a    ;

inc b    ;

sla d    ;┐

adc a,a  ;│

sla d    ;│

adc a,a  ;│

sla b    ;│

ld e,b   ;│3 раза

sli e    ;│

cp e     ;│

jr c,$+4 ;│

sub e    ;│

inc b    ;┘ t=3*63=189

sla h    ;┐

adc a,a  ;│

sla h    ;│

adc a,a  ;│

sla b    ;│

ld e,b   ;│2 раза

sli e    ;│

cp e     ;│

jr c,$+4 ;│

sub e    ;│

inc b    ;┘ t=63*2=126

ld d,l   ;

ld l,h   ;

ld h,a   ;меняем регистры

or a     ;  t=16

sbc hl,bc;┐

jr nc,$+3;│

add hl,bc;│

ccf      ;│2 раза

rl b     ;│

add hl,hl;│

add hl,hl;┘ t=67*2=134

ld l,h   ;есть подозрение, что

ld h,0   ;этот блок и предыдущий

ld c,b   ;можно немного ускорить

ld b,h   ;

rl h     ;

ld a,d   ;  t=31

rla      ;┐да и этот блок мне

adc hl,hl;│не нравится.

rla      ;│очень вероятно, что

adc hl,hl;│его можно неплохо

sla c    ;│ускорить. Но как?

rl b     ;│

ld d,b   ;│

ld e c   ;│3 раза

sli e    ;│

rl d     ;│

sbc hl,de;│

 jr nc,$+4;│

add hl,de;│

dec c    ;│

inc c    ;┘ t=3*119=357 (!)  ;ужас! Думайте, как ;ускорить!

rla      ;этот блок аналогичен

adc hl,hl;предыдущему с

rla      ;небольшой разницей

adc hl,hl;в конце. Это сделано

ld d,b   ;ради повышения

ld e,c   ;скорости вычислений

sla e    ;

rl d     ;

sli e    ;

rl d     ;

sbc hl,de;

ccf      ;  t=97

ld h,b   ;

ld l,c   ;

adc hl,hl;в hl - результат                 ;  t=23

;итого: 14+39+189+126+16+134+31+357+97+23=1026




Square table generator is based on observation that 

differences between consecutive squares 

(0, 1, 4, 9, 16, 25, ...)

form a sequence of odd numbers. 

(1, 3, 5, 7, 9, ...). 

Thus, by adding successive odd numbers 

iteratively we generate integer squares.

ld hl,SqrTab; must be a multiple of 256

ld b,l

ld c,l ; BC holds odd numbers

ld d,l

ld e,l ; DE holds squares

SqrGen ld (hl),e

inc h

ld (hl),d ; store x^2

ld a,l

neg

ld l,a

ld (hl),d

dec h

ld (hl),e ; store -x^2

ex de,hl

inc c

add hl,bc ; add next odd number

inc c

ex de,hl

cpl ; one byte replacement for NEG, DEC A

ld l,a

rla

jr c,SqrGen






; Square root of 16-bit value 

;by Ricardo Bittencourt

; In:  HL = value

; Out:  D = result (rounded down) 

Sqr16 ld de,#0040

ld a,l

ld l,h

ld h,d

or a

ld b,8

Sqr16_Loop sbc hl,de

jr nc,Sqr16_Skip

add hl,de

Sqr16_Skip ccf

rl d

add a,a

adc hl,hl

add a,a

adc hl,hl

djnz Sqr16_Loop

ret





; Square Root (16 bit)  HL=number to find 

;square root of Returns result in A

SQR16 LD DE,1

XOR A

DEC A

SQ16 SBC HL,DE

INC DE

INC DE

INC A

JR NC,SQ16

RET




; Square Root (32 bit) BCDE=number to find 

;square root of. Returns result in DE

SQR32 LD A,B 

PUSH DE 

POP IX 

LD D,0

LD E,D 

LD H,D 

LD L,D

LD B,16 

SQ32A SUB #40 

SBC HL,DE 

JR NC,SQ32B

ADD A,#40

ADC HL,DE

SQ32B CCF 

RL

RL

ADD IX,IX 

RL

RLA 

ADC HL,HL 

DJNZ SQ32A

RET 





sqrt32:

;Input: HLDE

;Output: DE is the sqrt, AHL is the remainder

;speed: 238+{0,1}+{0,44}+sqrtHL+3*sqrt32sub_2+sqrt32_iter15

;min: 1260

;max: 1506

;avg: 1377.75

  push de

  call sqrtHL

  pop bc

  add a,a

  ld e,a

  jr nc,+_

  inc d

_:

  ld a,b

  call sqrt32sub_2

  call sqrt32sub_2

;Now we have four more iterations

;The first two are no problem

  ld a,c

  call sqrt32sub_2

;On the next iteration, HL might temporarily overflow by 1 bit

  call sqrt32_iter15

;On the next iteration, HL is allowed to overflow,

 DE could overflow with our current routine, 

but it needs to be shifted right at the end, anyways

sqrt32_iter16:

  add a,a

  ld b,a        ;either 0x00 or 0x80

  adc hl,hl

  rla

  adc hl,hl

  rla

;AHL - (DE+DE+1)

  sbc hl,de \ sbc a,b

  inc e

  or a

  sbc hl,de \ sbc a,b

  ret p

  add hl,de

  adc a,b

  dec e

  add hl,de

  adc a,b

  ret

sqrt32sub_2:

;min: 185cc

;max: 231cc

;avg: 208cc

  call +_

_:

;min: 84cc

;max: 107cc

;avg: 95.5cc

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  inc e

  ret nc

  dec e

  add hl,de

  dec e

  ret

sqrt32_iter15:

;91+{8,0+{0,23}}

;min: 91cc

;max: 114cc

;avg: 100.75cc

  sll e \ rl d      ;sla e \ rl d \ inc e

  add a,a

  adc hl,hl

  add a,a

  adc hl,hl       ;This might overflow!

  jr c,sqrt32_iter15_br0

  sbc hl,de

  inc e

  ret nc

  dec e

  add hl,de

  dec e

  ret

sqrt32_iter15_br0:

  or a

  sbc hl,de

  inc e

  ret






sqrtA: ;Written by Zeda

;Input: A

;Output: D is the squareroot, A is the remainder (input-D^2)

;Destroys: E

;speed: 118+{0,6}+{0,7}+{0,7}+{0,3}

;min: 118cc

;max: 141cc

;avg: 129.5cc

;38 bytes

  ld de,5040h       ; 10

  sub e             ; 4

  jr nc,sqrtA_skip1 ;\

  add a,e           ; | branch 1: 12cc

  ld d,10h          ; | branch 2: 18cc

sqrtA_skip1:        ;/

; ----------

  cp d              ; 4

  jr c,sqrtA_skip2  ;\

  sub d             ; | branch 1: 12cc

  set 5,d           ; | branch 2: 19cc

sqrtA_skip2:        ;/

; ----------

  res 4,d           ; 8

  srl d             ; 8

  set 2,d           ; 8

  cp d              ; 4

  jr c,sqrtA_skip3  ;\

  sub d             ; | branch 1: 12cc

  set 3,d           ; | branch 2: 19cc

sqrtA_skip3:        ;/

  srl d             ; 8

; ----------

  inc a             ; 4

  sub d             ; 4

  jr nc,sqrtA_skip4 ;\

  dec d             ; | branch 1: 12cc

  add a,d           ; | branch 2: 15cc

sqrtA_skip4:        ;/

  srl d             ; 8

  ret               ; 10





sqrtDE:;returns HL as the sqrt, DE as the remainder

;33 bytes;min: 928cc;max: 1120cc;avg: 1024cc;928+8{24,0}

  ld b,$80

  xor a

  ld h,a

  ld l,a

sqrt_loop:

  srl b

  rra

  ld c,a

  add hl,bc

  ex de,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  ex de,hl

  or a

  sbc hl,bc

  .db $DA   ;start of jp c,** which is 10cc to skip the next two bytes.

_:

  ex de,hl

  add hl,bc

  srl h

  rr l

  srl b

  rra

  jr nc,sqrt_loop

  ret






sqrtfixed_88:

;Inputs: A.C

;Output: D.E contains the squareroot

;speed: 1482+12{0,17}

;min: 1482cc

;max: 1686cc

;avg: 1584cc 

;Adapted from Axe

;35 bytes

ld b,12

ld de,0

ld h,d

ld l,e

__Sqrt88Loop:

sub $40

sbc hl,de

jr nc,__Sqrt88Skip

add a,$40

adc hl,de

__Sqrt88Skip:

ccf

rl e

rl d

sla c

rla

adc hl,hl

sla c

rla

adc hl,hl

djnz __Sqrt88Loop

ret






sqrtfixed_88:

;Input: A.E ==> D.E

;Output: DE is the sqrt, AHL is the remainder

;Speed: 690+6{0,13}+{0,3+{0,18}}+{0,38}+sqrtA

;min: 855cc

;max: 1003cc

;avg: 924.5cc

;152 bytes

;Written by Zeda

  call sqrtA

  ld l,a

  ld a,e

  ld h,0

  ld e,d

  ld d,h

  sla e

  rl d

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

;Now we have four more iterations

;The first two are no problem

  sll e \ rl d

  add hl,hl

  add hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

  sll e \ rl d

  add hl,hl

  add hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

sqrtfixed_88_iter11:

;On the next iteration, HL might temporarily overflow by 1 bit

  sll e \ rl d      ;sla e \ rl d \ inc e

  add hl,hl

  add hl,hl

  jr c,sqrtfixed_88_iter11_br0

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  jr sqrtfixed_88_iter12

sqrtfixed_88_iter11_br0:

  or a

  sbc hl,de

_:

  inc e

;On the next iteration, HL is allowed to overflow,

 DE could overflow with our current routine, 

 but it needs to be shifted right at the end, anyways

sqrtfixed_88_iter12:

  ld b,a      ;A is 0, so B is 0

  add hl,hl

  add hl,hl

  rla

;AHL - (DE+DE+1)

  sbc hl,de \ sbc a,b

  inc e

  or a

  sbc hl,de \ sbc a,b

  ret p

  add hl,de

  adc a,b

  dec e

  add hl,de

  adc a,b

  ret





;Adapted from Axe

sqrtHL:;Input: HL;Output: D is the square root, 

cH is the remainder (c being the c flag), 

A is 0, B is 0, L is 0

;speed: 758+8{0,6}

;min: 758cc;max: 806cc;avg: 782cc;26 bytes

p_Sqrt:

ld a,l

ld l,h

ld de,$0040

ld h,d

ld b,8

or a

__SqrtLoop:

sbc hl,de

jr nc,__SqrtSkip

add hl,de

__SqrtSkip:

ccf

rl d

add a,a

adc hl,hl

add a,a

adc hl,hl

djnz __SqrtLoop

ret




;written by Zeda

sqrtHL:;returns A as the sqrt, HL as the remainder, D = 0 fastest

;min: 352cc;max: 391cc;avg: 371.5cc

  ld de,05040h  ; 10

  ld a,h        ; 4

  sub e         ; 4

  jr nc,sq7     ;\

  add a,e       ; | branch 1: 12cc

  ld d,16       ; | branch 2: 18cc

sq7:            ;/

  cp d          ; 4

  jr c,sq6      ;\

  sub d         ; | branch 1: 12cc

  set 5,d       ; | branch 2: 19cc

sq6:            ;/

  res 4,d       ; 8

  srl d         ; 8

  set 2,d       ; 8

  cp d          ; 4

  jr c,sq5      ;\

  sub d         ; | branch 1: 12cc

  set 3,d       ; | branch 2: 19cc

sq5:            ;/

  srl d         ; 8

  inc a         ; 4

  sub d         ; 4

  jr nc,sq4     ;\

  dec d         ; | branch 1: 12cc

  add a,d       ; | branch 2: 19cc

  dec d         ; | <-- this resets the low bit of D, so `srl d` resets carry.

sq4:            ;/

  srl d         ; 8

  ld h,a        ; 4

  ld a,e        ; 4

  sbc hl,de     ; 15

  jr nc,sq3     ;\

  add hl,de     ; | 12cc or 18cc

sq3:            ;/

  ccf           ; 4

  rra           ; 4

  srl d         ; 8

  rra           ; 4

  ld e,a        ; 4

  sbc hl,de     ; 15

  jr c,sq2      ;\

  or 20h        ; | branch 1: 23cc

  db 254        ; |   <-- start of `cp *` which is 7cc to skip the next byte.

sq2:            ; | branch 2: 21cc

  add hl,de     ;/

  xor 18h       ; 7

  srl d         ; 8

  rra           ; 4

  ld e,a        ; 4

  sbc hl,de     ; 15

  jr c,sq1      ;\

  or 8          ; | branch 1: 23cc

  db 254        ; |   <-- start of `cp *` which is 7cc to skip the next byte.

sq1:            ; | branch 2: 21cc

  add hl,de     ;/

  xor 6         ; 7

  srl d         ; 8

  rra           ; 4

  ld e,a        ; 4

  sbc hl,de     ; 15

  jr nc,+_      ;    \

  add hl,de     ; 15  |

  srl d         ; 8   |

  rra           ; 4   | branch 1: 38cc

  ret           ; 10  | branch 2: 40cc

_:              ;     |

  inc a         ; 4   |

  srl d         ; 8   |

  rra           ; 4   |

  ret           ; 10 /





; fast 16-bit isqrt by Zeda Thomas; Feel free to use for whatever :) 

very fastest

sqrtHL:

;Input: HL;Output: A is the integer square root of HL

;Destroys: HL,DE (D is actually 0)

;min: 343cc

;max: 380cc

;avg: 361.5cc

;88 bytes

  ld de,05040h  ; 10

  ld a,h        ; 4

  sub e         ; 4

  jr nc,sq7     ;\

  add a,e       ; | branch 1: 12cc

  ld d,16       ; | branch 2: 18cc

sq7:            ;/

; ----------

  cp d          ; 4

  jr c,sq6      ;\

  sub d         ; | branch 1: 12cc

  set 5,d       ; | branch 2: 19cc

sq6:            ;/

; ----------

  res 4,d       ; 8

  srl d         ; 8

  set 2,d       ; 8

  cp d          ; 4

  jr c,sq5      ;\

  sub d         ; | branch 1: 12cc

  set 3,d       ; | branch 2: 19cc

sq5:            ;/

  srl d         ; 8

; ----------

  inc a         ; 4

  sub d         ; 4

  jr nc,sq4     ;\

  dec d         ; | branch 1: 12cc

  add a,d       ; | branch 2: 19cc

  dec d         ; | <-- this resets the low bit of D, so `srl d` resets carry.

sq4:            ;/

  srl d         ; 8

  ld h,a        ; 4

; ----------

  ld a,e        ; 4

  sbc hl,de     ; 15

  jr nc,sq3     ;\

  add hl,de     ; | 12cc or 18cc

sq3:            ;/

  ccf           ; 4

  rra           ; 4

  srl d         ; 8

  rra           ; 4

; ----------

  ld e,a        ; 4

  sbc hl,de     ; 15

  jr c,sq2      ;\

  or 20h        ; | branch 1: 23cc

  db 254        ; |   <-- start of `cp *` which is 7cc to skip the next byte.

sq2:            ; | branch 2: 21cc

  add hl,de     ;/

  xor 18h       ; 7

  srl d         ; 8

  rra           ; 4

; ----------

  ld e,a        ; 4

  sbc hl,de     ; 15

  jr c,sq1      ;\

  or 8          ; | branch 1: 23cc

  db 254        ; |   <-- start of `cp *` which is 7cc to skip the next byte.

sq1:            ; | branch 2: 21cc

  add hl,de     ;/

  xor 6         ; 7

  srl d         ; 8

  rra           ; 4

; ----------

  ld e,a        ; 4

  sbc hl,de     ; 15

;This code would restore the square root

;   jr nc,sq0      ;\

;   add hl,de     ; | 12cc or 18cc

; sq0:            ;/

  sbc a,255     ; 7

  srl d         ; 8

  rra           ; 4

  ret           ; 10





sqrtHLIX:

;Input: HLIX;Output: DE is the sqrt, AHL is the remainder

;speed: 751+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL

;min: 1103;max: 1237;avg: 1165.5;166 bytes

  call sqrtHL   ;expects returns A as sqrt, HL as remainder, D = 0

  add a,a

  ld e,a

  rl d

  ld a,ixh

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

;Now we have four more iterations

;The first two are no problem

  ld a,ixl

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

  sll e \ rl d

  add a,a \ adc hl,hl

  add a,a \ adc hl,hl

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  .db $FE     ;start of `cp *`

_:

  inc e

sqrt32_iter15:

;On the next iteration, HL might temporarily overflow by 1 bit

  sll e \ rl d      ;sla e \ rl d \ inc e

  add a,a

  adc hl,hl

  add a,a

  adc hl,hl       ;This might overflow!

  jr c,sqrt32_iter15_br0

  sbc hl,de

  jr nc,+_

  add hl,de

  dec e

  jr sqrt32_iter16

sqrt32_iter15_br0:

  or a

  sbc hl,de

_:

  inc e

;On the next iteration, HL is allowed to overflow, 

DE could overflow with our current routine, 

but it needs to be shifted right at the end, anyways

sqrt32_iter16:

  add a,a

  ld b,a        ;either 0x00 or 0x80

  adc hl,hl

  rla

  adc hl,hl

  rla

;AHL - (DE+DE+1)

  sbc hl,de \ sbc a,b

  inc e

  or a

  sbc hl,de \ sbc a,b

  ret p

  add hl,de

  adc a,b

  dec e

  add hl,de

  adc a,b

  ret





SqrtL:

;Inputs:;     L is the value to find the square root of

;Outputs:

;      C is the result

;      B,L are 0

;      DE is not changed

;      H is how far away it is from the next smallest perfect square

;      L is 0

;      z flag set if it was a perfect square;Destroyed:      A

     ld bc,400h       ; 10    10

     ld h,c           ; 4      4

sqrt8Loop:            ;

     add hl,hl        ;11     44

     add hl,hl        ;11     44

     rl c             ; 8     32

     ld a,c           ; 4     16

     rla              ; 4     16

     sub a,h          ; 4     16

     jr nc,$+5        ;12|19  48+7x

       inc c

       cpl

       ld h,a

     djnz sqrt8Loop   ;13|8   47

     ret              ;10     10

;287+7x, x is the number of bits in the result;min: 287;max: 315;19 bytes




L_sqrd:

;Input: L;Output: L*L->A

;147 t-states;36 bytes

    ld b,l

;First iteration, get the lowest 3 bits of -x^2

    sla l

    rrc b

    sbc a,a

    or l

    ld c,a

;second iteration, get the next 2 bits of -x^2

    rrc b

    sbc a,a

    xor l

    and $F8

    add a,c

    ld c,a

;third iteration, get the next 2 bits of -x^2

    sla l

    rrc b

    sbc a,a

    xor l

    and $E0

    add a,c

    ld c,a

;fourth iteration, get the eight bit of x^2

    sla l

    rrc b

    sbc a,a

    xor l

    and $80

    sub c

    ret




sqrA:

;A*A->A

;Destroys: HL

;76cc or 79cc or 82cc

;Avg: 79cc;51 bytes

    add a,a

    add a,a

    jr nc,$+4

    neg

    rrca

    rrca

    ld l,a

    srl l

    ld h,sqrLUT/256

    jr c,$+4

    neg

    add a,(hl)

    ret

sqrLUT:

;MUST BE ALIGNED to a 256-byte boundary.

;Can use:

;  #if 0!=$&255

;  .fill 256-($&255),0

;  #endif

.db $00,$06,$14,$2A,$48,$6E,$9C,$D2

.db $10,$56,$A4,$FA,$58,$BE,$2C,$A2

.db $20,$A6,$34,$CA,$68,$0E,$BC,$72

.db $30,$F6,$C4,$9A,$78,$5E,$4C,$42





;|HL = SQR (HL)

;4.RAS - квадратный корень

;INPUT : HL <-- из чего

;OUTPUT: HL = SQR (HL)

;портятся  DE,HL,BC,A

RAS     LD      A,H

        OR      L

        JR      Z,RAS_4

RAS1     LD      A,H

        AND     A

        JP      NZ,RAS11

        LD      A,L

        CP      1

        JP      NZ,RAS11

        LD      HL,1

        RET 

RAS11   LD      B,H

        LD      C,L

        SRL     B

        RR      C

RAS_1   PUSH    HL

        LD      D,B

        LD      E,C

        CALL    DIV

        ADD     HL,BC

        SRL     H

        RR      L

        PUSH    HL

        LD      D,B

        LD      E,C

        SBC     HL,DE

        JP      NC,RES_10

        ADD     HL,DE

        EX      DE,HL

        SBC     HL,DE

RES_10  LD      A,H

        AND     A

        JP      NZ,RAS_0

        LD      A,L

        CP      2

        JP      NC,RAS_0

        POP     HL

        POP     BC

        RET 

RAS_0   POP     BC

        POP     HL

        JP      RAS_1

RAS_4   LD      HL,0

        RET 


SQR     OR A

        LD A,L

        LD L,H

        LD DE,64

        LD H,D

        LD B,7

SQR0    SBC HL,DE

        JR NC,$+3

        ADD HL,DE

        CCF 

        RL D

        RLA 

        ADC HL,HL

       JP C,0

        RLA 

        ADC HL,HL

       JP C,0

        DJNZ SQR0

        SBC HL,DE

       ;JR NC,$+3

       ;ADD HL,DE

        CCF 

        RL D

       ;RLA

       ;ADC HL,HL

       ;RLA

       ;ADC HL,HL

        RET 






;sqr hl (from Inferno 4)

;HL=аргумент N

    OR A

    LD A,L

    LD L,H

    LD DE,64

    LD H,D

    LD B,8

sqr0    SBC HL,DE

    JR NC,$+3

    ADD HL,DE

    CCF

    RL D

    ADD A,A

    ADC HL,HL

    ADD A,A

    ADC HL,HL

    DJNZ sqr0

;D=результат


NEG

neghl   xor a

        sub l

        ld l,a

        sbc a,h;a,a

        sub l

        ld h,a

        ret



negde   xor a

        sub e

        ld e,a

        sbc a,d ;a,a

        sub e

        ld d,a

        ret



RANDOM

;by DDp

;in:    HL', DE'   out:   A, HL', DE'

RANDOM

EXX

LD      C,L

LD      A,H

RL      C

RLA

RL      C

RLA

LD      B,A

RL      C

RLA

RL      C

RLA

RL      C

RLA

XOR     B

LD      H,L

LD      L,D

LD      D,E

LD      E,A

EXX

RET



Процедура jtn’а для двухбайтного рандома, с сидом. 

Для однобайтного можно брать любой байт из двух:

rnd16

    ld de,seed

    ld a,d,h,e,l,253

    or a:sbc hl,de,a,0,hl,de

    ld d,0:sbc a,d:ld e,a:sbc hl,de

    jr nc,rnd:inc hl

rnd ld (rnd16+1),hl

    ret




random_color

ld a,r

and 7

or a

jr z,random_color

ret



Xorshift

Xorshift is a class of pseudorandom number generators 

discover by George Marsaglia and detailed in his 

2003 paper, Xorshift RNGs.

; 16-bit xorshift pseudorandom number generator 

by John Metcalf, 20 bytes, 86 cycles (excluding ret)

; returns   hl = pseudorandom number; corrupts a

; generates 16-bit pseudorandom numbers 

with a period of 65535

; using the xorshift method: 

hl ^= hl << 7; hl ^= hl >> 9; hl ^= hl << 8

; some alternative shift triplets which also 

perform well are: 6, 7, 13; 7, 9, 13; 9, 7, 13.

xornd ld hl,1       ; seed must not be 0

ld a,h

rra

ld a,l

rra

xor h

ld h,a

ld a,l

rra

ld a,h

rra

xor l

ld l,a

xor h

ld h,a

ld (xornd+1),hl

ret




;You may use this routine, just be sure to 

credit John Metcalf! Written by John Metcalf

;http://www.retroprogramming.com/2017/07/xorshift-pseudorandom-numbers-in-z80.html

; Annotated by Zeda Thomas, 

fixed typo (86 cycles==> 82 cycles)

;Note: uses SMC 16-bit xorshift pseudorandom 

number generator

; 20 bytes, 82 cycles (excluding ret)

; returns   hl = pseudorandom number

; corrupts   a

xorrnd

  ld hl,1         ; Init the seed, must not be 0

  ld a,h          ;\

  rra             ; | Get the top bits of xs<<7 and xor with the top byte of HL

  ld a,l          ; |        abcdefgh ijklmnop

  rra             ; |       ^hijklmno 00000000

  xor h           ; | Note that we still need to xor the 'p' with the top byte of l

  ld h,a          ;/

  ld a,l          ;\

  rra             ; | we get 'p' in the carry flag, now shift that in when we do xs>>9

  ld a,h          ; |        abcdefgh ijklmnop   (new value)

  rra             ; |       ^00000000 pabcdefg

  xor l           ; | the 'p' is leftover from the first step, so now Step 1 and 2 are done

  ld l,a          ;/

  xor h           ;\ Finally, xor the bottom byte with the top byte for step 3

  ld h,a          ;/

  ld (xorrnd+1),hl  ; write back the new value as the next seed

  ret




;Dean Belfield

; 16 bit random number routine I found on the web

; Returns a pseudo-random number in the HL register

RND16_SEED: EQU 12345

RND16: LD DE,RND16_SEED

LD A,D

LD H,E

LD L,253

OR A

SBC HL,DE

SBC A,0

SBC HL,DE

LD D,0

SBC A,D

LD E,A

SBC HL,DE

JR NC,1F

INC HL

1f LD (RND16+1),HL

RET





RND_32  LD    HL,(SEED)

        CALL  RND

RND     LD    A,H

        ADD   HL,HL

        XOR   H

        ADD   HL,HL

        ADD   HL,HL

        ADD   HL,HL

        XOR   H

        ADD   HL,HL

        ADD   HL,HL

        XOR   H

        ADD   HL,HL

        ADD   HL,HL

        LD    L,A

        LD    (SEED),HL

        RET 

SEED    DEFW  #FFFF ; НЕ НОЛЬ!






Следующая процедура является генератором RND. 

На выходе из процедуры в

регистровой паре HL будет число от 0 до 65535.


 10         ORG     40000

 20 BEGIN   LD      HL,(TEMP)

 30         LD      C,16

 40 S1      LD      A,H

 50         ADD     HL,HL

 60         AND     255

 70         JP      PE,S2

 80         INC     HL

 90 S2      DEC     C

100         JR      NZ,S1

110         LD      (TEMP),HL

120 TEMP    DEFB    1,0






from Pplayers (Saracen)

RANDOM LD DE,(SEED)

LD H,E

LD L,253

LD A,D

OR A

LD B,0

SBC HL,DE

SBC A,B

SBC HL,DE

SBC A,B

LD E,A

LD D,B

SBC HL,DE

JP NC,RANDOM2

INC HL

RANDOM2 LD (SEED),HL

RET



by Baze

RANDOM    

ld a,SEED ;SEED is usually 0

    ld b,a

    add a,a

    add a,a

    add a,b

    inc a ;you may try also ADD A,7

    ld (RANDOM+1),a

    ret




 8-bit Xor-Shift random number generator.

;Created by Patrik Rak in 2008 and revised in 2011/2012.

;http://www.worldofspectrum.org/forums/showthread.php?t=23070


        org 40000


        call rnd        ; BASIC driver

        ld   c,a

        ld   b,0

        ret


xsrnd   ld  hl,0xA280   ; yw -> zt

        ld  de,0xC0DE   ; xz -> yw

        ld  (rnd+4),hl  ; x = y, z = w

        ld  a,l         ; w = w ^ ( w << 3 )

        add a,a

        add a,a

        add a,a

        xor l

        ld  l,a

        ld  a,d         ; t = x ^ (x << 1)

        add a,a

        xor d

        ld  h,a

        rra             ; t = t ^ (t >> 1) ^ w

        xor h

        xor l

        ld  h,e         ; y = z

        ld  l,a         ; w = t

        ld  (xsrnd+1),hl

        ret 





pentis by tony raugas (далеко не самый лучший вариант)

; ;---

; ld      a, r ;рандом но он раскидан 

; dec     a ;

; adc     a, 1

; ld      (random_var), a ;

; ld      hl, (random_var);теперь объеденил и рандом заработал как надо

; ld      b, 5   ;раньше выдавал первую фигуру в тетрисе Т в  петрисе L в пентисе Y (не суть)

; frn1 sla     l

; rl      h ;ВНИМАНИЯ-ВНИМАНЬЯ! рандом в самый первый раз НАДО ВЫЗВАТЬ ДВАЖДЫ!!!

; ld      a, #C0 ;что в принципе и сделано

; and     h ;зато после удаления фигур и режимов петриса и пентиса - заглючило опять

; jp      pe, frn2

; inc     l

; frn2 djnz    frn1

; ld      (random_var), hl







8-bit Random Number Generator

This is a very simple linear congruential generator.

The formula is x[i + 1] = (5 * x[i] + 1) mod 256.

Its only advantage is small size and simplicity.

Due to nature of such generators only a couple 

of higher bits should be considered random.

Input: none ,

Output: A = pseudo-random number, period 256

Rand8 ld a,Seed ; Seed is usually 0

ld b,a

add a,a

add a,a

add a,b

inc a ; another possibility is ADD A,7

ld (Rand8+1),a

ret




;This code snippet is 9 bytes and 43cc

;Inputs:;   HL is the input seed and must be non-zero

;Outputs:;   A is the 8-bit pseudo-random number

;   HL is the new seed value (will be non-zero)

;opcode cc

    add hl,hl     ; 29    11

    sbc a,a       ; 9F     4

    and %00101101 ; E62D   7

    xor l         ; AD     4

    ld l,a        ; 6F     4

    ld a,r        ; ED5F   9

    add a,h       ; 84     4

;Technical details:

;   The concept behind this routine is to combine an LFSR (poor RNG) with a

; counter. The counter improves the RNG quality, while also extending the period

; length.

;   For this routine, I took advantage of the Z80's built-in counter, the `r`

; register. This means that we don't need to store the counter anywhere, and it

; is pretty fast to access!

;   Some caveats:

;     * r is a 7-bit counter

;     * r will increment some number of times between runs of the RNG. In most

;       cases, this will be constant, but if it increments an even number each

;       time, then the bottom bit is always the same, weakening the effect of

;       the counter. In the worst case, it increments a multiple of 128 times,

;       effectively making your RNG just as good/bad as the LFSR. Ideally, you

;       want `r` to increment an odd number of times between runs.

;     * In the best case, the bottom 7 bits have 50/50 chance of being 0 or 1.

;       The top bit is 1 with probability 1/2 + 1/(2^17-2) ~ .5000076295

;     * In the event that your main loop waits for user input between calls,

;       then congatulations, you might have a True RNG :)





Fast quality CMWC random number generator for Z80 

I have created to weed out the crap RNGs out there. 

32+10 bytes, 146..149 T cycles, period 253*2^59, 

which is more than 2^66 or 10^20.

cmwc.asm

; 8-bit Complementary-Multiply-With-Carry (CMWC) 

random number generator.

; Created by Patrik Rak in 2012, and revised in 2014/2015,

; with optimization contribution 

from Einar Saukas and Alan Albrecht.

;http://www.worldofspectrum.org/forums/showthread.php?t=39632

        org 40000

        call rnd    ; BASIC driver

        ld   c,a

        ld   b,0

        ret

rnd     ld   hl,table

        ld   a,(hl) ; i = ( i & 7 ) + 1

        and  7

        inc  a

        ld   (hl),a

        inc  l      ; hl = &cy

        ld   d,h    ; de = &q[i]

        add  a,l

        ld   e,a

        ld   a,(de) ; y = q[i]

        ld   b,a

        ld   c,a

        ld   a,(hl) ; ba = 256 * y + cy

        sub  c      ; ba = 255 * y + cy

        jr   nc,$+3

        dec  b

        sub  c      ; ba = 254 * y + cy

        jr   nc,$+3

        dec  b

        sub  c      ; ba = 253 * y + cy

        jr   nc,$+3

        dec  b

        ld   (hl),b ; cy = ba >> 8, x = ba & 255

        cpl         ; x = (b-1) - x = -x - 1 = ~x + 1 - 1 = ~x

        ld   (de),a ; q[i] = x

        ret

table   db   0,0,82,97,120,111,102,116,20,15

        if   (table/256)-((table+9)/256)

        error "whole table must be within single 256 byte block"

        endif






4.2 16-bit Random Number Generator

This generator is based on similar method but

gives much better results. 

It was taken from an old ZX Spectrum 

game and slightly optimised.

Input: none

Output: HL = pseudo-random number, period 65536

Rand16 ld de,Seed ; Seed is usually 0

ld a,d

ld h,e

ld l,253

or a

sbc hl,de

sbc a,0

sbc hl,de

ld d,0

sbc a,d

ld e,a

sbc hl,de

jr nc,Rand

inc hl

Rand ld (Rand16+1),hl

ret





Z80 Psuedo-random number generator

ZX Spectrum Random Numbers

LD A,R ; Load the A register with the refresh register

LD L,A ; Copy register A into register L

AND %00111111 ; This masking prevents the address we are forming from accessing RAM

LD H,A ; Copy register A into register H

LD A,(HL) ; Load the pseudo-random value into A

; HOW THIS WORKS

; The refresh register in the Z80 is highly 

unpredictable since it is incremented every cycle.

; Because it may be at any value when this 

routine is called, it is very good for random numbers.

; This routine increases the randomness of the 

number since it forms an address based on the

; refresh counter's current status and accesses 

the memory at that address.

; It can also be modified to get a sixteen-bit 

pseudo-random number by changing line 5 to LD D,(HL)

; and adding these two lines to the end:

; INC L

; LD E,(HL)

; This routine was written for the ZX Spectrum 

which has a 16KB ROM. If you plan to use this routine

; on another Z80 system, change the binary value at 

the AND instruction. For example, if you had a

; Z80 computer with an 8KB ROM, you would change the 

binary value to %00011111.






; Fast RND

; An 8-bit pseudo-random number generator,

; using a similar method to the Spectrum ROM,

; - without the overhead of the Spectrum ROM.

; R = random number seed

; an integer in the range [1, 256]

; R -> (33*R) mod 257

; S = R - 1

; an 8-bit unsigned integer

 ld a, (seed)

 ld b, a 

 rrca ; multiply by 32

 rrca

 rrca

 xor #1f

 add a, b

 sbc a, 255 ; carry

 ld (seed), a

 ret





This is a short peice of code that generates a maximal length,

8 bit, pseudo random number sequence. 

It was originally used to switch off and on all the LED's in a 8x30 matrix 

in a good random looking order (the un-needed values being skipped).

It's shorter, but slower, than using a lookup table and a counter.

; returns pseudo random 8 bit number in A. Only affects A.

; (r_seed) is the byte from which the number is generated and MUST be

; initialised to a non zero value or this function will always return

; zero. Also r_seed must be in RAM, you can see why......

rand_8 LD A,(r_seed) ; get seed

AND #B8h ; mask non feedback bits

SCF ; set carry

JP PO,no_clr ; skip clear if odd

CCF ; complement carry (clear it)

no_clr LD A,(r_seed) ; get seed back

RLA ; rotate carry into byte

LD (r_seed),A ; save back for next prn

RET ; done

r_seed DB 1 ; prng seed byte (must not be zero)






Ion Random

This is based off the tried and true pseudorandom 

number generator featured in Ion by Joe Wingbermuehle

;-----> Generate a random number

; output a=answer 0<=a<=255

; all registers are preserved except: af

random:

        push    hl

        push    de

        ld      hl,(randData)

        ld      a,r

        ld      d,a

        ld      e,(hl)

        add     hl,de

        add     a,l

        xor     h

        ld      (randData),hl

        pop     de

        pop     hl

        ret

randData dw 12345 ;here must be a 2 byte seed located in ram. 

While this is a fast generator, 

it's generally not considered very good 

in terms of randomness.





Linear Feedback Shift Register

This particular prng is based on Linear 

feedback shift register.

It uses a 64bit seed and generates 8 new 

bits at every call. 

LFSRSeed must be an 8 byte seed located in ram.

;------LFSR------

;James Montelongo

;optimized by Spencer Putt

;out: a = 8 bit random number

RandLFSR ld hl,LFSRSeed+4

        ld e,(hl)

        inc hl

        ld d,(hl)

        inc hl

        ld c,(hl)

        inc hl

        ld a,(hl)

        ld b,a

        rl e \ rl d

        rl c \ rla

        rl e \ rl d

        rl c \ rla

        rl e \ rl d

        rl c \ rla

        ld h,a

        rl e \ rl d

        rl c \ rla

        xor b

        rl e \ rl d

        xor h

        xor c

        xor d

        ld hl,LFSRSeed+6

        ld de,LFSRSeed+7

        ld bc,7

        lddr

        ld (de),a

        ret

While this may produces better numbers, 

it is slower, larger and requires a bigger seed

than ionrandom. Assuming theres is a good seed to start,

it should generate ~2^56 bytes before repeating. However if t

here is not a good seed(0 for example), then the numbers crea

ted will not be adequate. Unlike Ionrandom and its use of the 

r register, starting with the same seed the same numbers will 

be generated. With Ionrandom 

the code running may have an impact on the number generated. 

This means this method requires more initialization.

You can initialize with TI-OS's seeds, stored at 

seed1 and seed2, both are ti-floats but will serve the purpose.






Combined LFSR/LCG, 16-bit seeds

This is a very fast, quality pseudo-random number generator. 

It combines a

16-bit Linear Feedback Shift Register 

and a 16-bit LCG.

prng16:

;Inputs:

;   (seed1) contains a 16-bit seed value

;   (seed2) contains a NON-ZERO 16-bit seed value

;Outputs:

;   HL is the result

;   BC is the result of the LCG, so not that great of quality

;   DE is preserved

;Destroys:

;   AF

;cycle: 4,294,901,760 (almost 4.3 billion)

;160cc

;26 bytes

    ld hl,(seed1)

    ld b,h

    ld c,l

    add hl,hl

    add hl,hl

    inc l

    add hl,bc

    ld (seed1),hl

    ld hl,(seed2)

    add hl,hl

    sbc a,a

    and %00101101

    xor l

    ld l,a

    ld (seed2),hl

    add hl,bc

    ret

On their own, LCGs and LFSRs don't pr

oduce great results and are generally

 very cyclical, but they are very fast 

to compute. The 16-bit LCG in the above 

example will bounce around and reach each

 number from 0 to 65535, but the lower bits 

are far more predictable than the upper bits. Th

e LFSR mixes up the predictability of a given bit'

s state, but it hits every number except 0, meanin

g there is a slightly higher chance of any given b

it in the result being a 1 instead of a 0. It turn

s out that by adding together the outputs of these 

two generators, we can lose the predictability of a

 bit's state, while ensuring it has a 50% chance of

 being 0 or 1. As well, since the periods, 

65536 and 65535 are coprime, then the overall period of 

the generator is 65535*65536, which is over 4 billion.







Combined LFSR/LCG, 32-bit seeds

This is similar to the one above, except that it use

s 32-bit seeds (and still returns a 16-bit result). 

An advantage here is that they've been tested and 

passed randomness tests (all of thee ones offered 

by CAcert labs). As well, it is still very fast.

rand32:

;Inputs:

;   (seed1_0) holds the lower 16 bits of the first seed

;   (seed1_1) holds the upper 16 bits of the first seed

;   (seed2_0) holds the lower 16 bits of the second seed

;   (seed2_1) holds the upper 16 bits of the second seed

;   **NOTE: seed2 must be non-zero

;Outputs:

;   HL is the result

;   BC,DE can be used as lower quality values, 

;   but are not independent of HL.

;Destroys:

;   AF

;Tested and passes all CAcert tests

;Uses a very simple 32-bit LCG and 32-bit LFSR

;it has a period of 18,446,744,069,414,584,320

;roughly 18.4 quintillion.

;LFSR taps: 0,2,6,7  = 11000101

;291cc

seed1_0=$+1

    ld hl,12345

seed1_1=$+1

    ld de,6789

    ld b,h

    ld c,l

    add hl,hl \ rl e \ rl d

    add hl,hl \ rl e \ rl d

    inc l

    add hl,bc

    ld (seed1_0),hl

    ld hl,(seed1_1)

    adc hl,de

    ld (seed1_1),hl

    ex de,hl

seed2_0=$+1

    ld hl,9876

seed2_1=$+1

    ld bc,54321

    add hl,hl \ rl c \ rl b

    ld (seed2_1),bc

    sbc a,a

    and %11000101

    xor l

    ld l,a

    ld (seed2_0),hl

    ex de,hl

    add hl,bc

    ret





rand32:

;Tested and passes all CAcert tests

;Uses a very simple 32-bit LCG and 32-bit LFSR

;it has a period of 18,446,744,069,414,584,320

;roughly 18.4 quintillion.

;LFSR taps: 0,2,6,7  = 11000101

;291cc

;Thanks to Runer112 for his help on optimizing the LCG 

and suggesting to try the much simpler LCG. On their own,

 the two are terrible, but together they are great.

;58 bytes

seed1_0=$+1

    ld hl,12345

seed1_1=$+1

    ld de,6789

    ld b,h

    ld c,l

    add hl,hl \ rl e \ rl d

    add hl,hl \ rl e \ rl d

    inc l

    add hl,bc

    ld (seed1_0),hl

    ld hl,(seed1_1)

    adc hl,de

    ld (seed1_1),hl

    ex de,hl

;;lfsr

seed2_0=$+1

    ld hl,9876

seed2_1=$+1

    ld bc,54321

    add hl,hl \ rl c \ rl b

    ld (seed2_1),bc

    sbc a,a

    and %11000101

    xor l

    ld l,a

    ld (seed2_0),hl

    ex de,hl

    add hl,bc

    ret





rand24:

;;219cc

#ifdef smc

seed1_0=$+1

    ld hl,12345

seed1_1=$+1

    ld a,67

#else

    ld hl,(seed1_0)

    ld a,(seed1_1)

#endif

    ld b,h

    ld c,l

    ld d,a

    add hl,hl \ rla

    add hl,hl \ rla

    inc l

    add hl,bc \ adc a,0

    ld (seed1_0),hl

    ld (seed1_1),a

    ld c,b

    ld b,a

#ifdef smc

seed2_0=$+1

    ld hl,65432

seed2_1=$+1

    ld a,10

#else

    ld hl,(seed2_0)

    ld a,(seed2_1)

#endif

    add hl,hl

    rla

    ld (seed2_1),a

    sbc a,a

    and %10000111

    xor l

    ld l,a

    ld (seed2_0),hl

    add hl,bc

    ret





;#define smc ;uncomment if you are using SMC

rand16:

;collaboration by Zeda with Runer112

;160cc or 148cc if using SMC

;26 bytes

;cycle: 4,294,901,760 (almost 4.3 billion)

#ifdef smc

seed1=$+1

ld hl,9999

#else

    ld hl,(seed1)

#endif

    ld b,h

    ld c,l

    add hl,hl

    add hl,hl

    inc l

    add hl,bc

    ld (seed1),hl

#ifdef smc

seed2=$+1

ld hl,9999

#else

    ld hl,(seed2)

#endif

    add hl,hl

    sbc a,a

    and %00101101

    xor l

    ld l,a

    ld (seed2),hl

    add hl,bc

    ret




rand10:

;Returns A as a random integer on [0,9]

;Destroys: All

  call rand5

  sla h

  rla

  ret


rand5:

;Returns A on [0,4]

;Destroys: All

;Notes:

; This is a non-standard approach to generating random integers on [0,4].

; If you have a truly random number generator that generates bits (0 or 1)

; with equal probability, then standard approaches will still cause a slight

; bias. ("Standard": "rand mod 5" or int(5*rand)). For example, suppose we

; generate a 4-bit number. Then "rand mod 5" will cause 0 to be chosen

; 4/16 times, while 1, 2, 3, and 4 will be chosen 3/16 times (on average).

; A similar problem exists with int(5*rand). One way to mitigate this issue

; is just generating infintely many bits, but apparently that is impractical,

; so I came up with a compromise.

; My approach basically looks at the binary expansion of 1/5, 2/5, 3/5, and 4/5.

;   1/5 = .0011001100110011...

;   2/5 = .0110011001100110...

;   3/5 = .1001100110011001...

;   4/5 = .1100110011001100...

; So if I generate random bits and I get .001100, then a 0, then I know

; that no matter what all of the rest of the bits are, the number is less than

; 1/5, and so int(5*rand) is 0.

; By applying similar logic to the rest of the values, I can guarantee a uniform

; distribution on [0,4]. But there are four cases where this process might

; continue forever, specifically the cases that are like ...00110011...., but

; lucky for us, this happens 4/inf= 0% of the time. In fact, on average it

; takes 3 to 4 bits before the algorithm can assert which value to return.

; The one caveat is that on the Z80, we generally don't have truly random

; numbers :| On the otherhand, it is easy enough to generate pseudo-random

; bits with equal probability :)

  call rand

  ld a,h

  and $C0

  push af    ;save the original value

  ld c,a

rand5_start:

  push bc

  call rand

  pop bc

  ld b,15    ;I set this to 15 because I like to guarantee a bit is available for rand10.

rand5_loop:

  ld a,h

  xor c

  jp p,rand5_end

  add hl,hl

  sla c

  jr c,$+4

  set 6,c

  djnz rand5_loop

  jr rand5_start

rand5_end:

  pop af

  rlca

  rlca

  sla h

  adc a,0

  ret





lfsr64:

;;Output: A is an 8-bit pseudo-random number.

    ld hl,seed

    sla (hl) \ inc hl

    rl (hl) \ inc hl

    rl (hl) \ inc hl

    rl (hl) \ inc hl

    rl (hl) \ inc hl

    rl (hl) \ inc hl

    rl (hl) \ inc hl

    rl (hl)

    ret nc

    ld a,(seed)

    xor %000011011

    ld (seed),a

    ret




lehmer:

;Input:

;  (seed) has the seed value of the RNG

;Output:

;  (seed) is updated, HL is the result

;Destroys:

;  A,DE,BC

;Timing:

;  if seed>0        231cc or 232cc, condition dependent

;  if seed=0        91cc

;  if SMC defined   subtract 6cc

;Size: 44 bytes

;Notes:

;    Uses the Lehmer RNG used by the Sinclair ZX81

;    75x mod 65537 -> x

#ifndef SMC

    ld hl,(seed)

#else

seed = $+1

    ld hl,0

#endif

;multiply by 75

    ld c,l

    ld b,h

    xor a

    adc hl,hl \ jr z,special \ ld d,a \ rla

    add hl,hl \ rla

    add hl,hl \ rla \ add hl,bc \ adc a,d

    add hl,hl \ rla

    add hl,hl \ rla \ add hl,bc \ adc a,d

    add hl,hl \ rla \ add hl,bc

;modulo 65537, see note below on how this works

    ld e,a

    sbc hl,de       ;No need to reset the c flag since it is already

    jr nc,$+3

    inc hl

    ld (seed),hl

    ret

special:

;In the case that HL=0, 

this should be interpreted 

as 65536 = -1 mod 65537, 

so return -75 mod 65537 = -74 mod 65536 in HL

    ld hl,-74

    ld (seed),hl

    ret

;mod by 2^16 + 1 (a prime)

;current form is A*2^16+HL

;need:

;  (A*2^16+HL) mod (2^16+1)

;add 0 as +1-1

;  (A*(2^16+1-1)+HL) mod (2^16+1)

;distribute

;  (A*(2^16+1)-A+HL) mod (2^16+1)

;A*(2^16+1) mod 2^16+1 = 0, so remove

;  (-A+HL) mod (2^16+1)

;Oh hey, that's easy! :P

;I use this trick everywhere, you should, too.





(из zx-review):

RND LD HL,0

LD A,H

ADD A,#77

LD H,A

RLС L

ADD A,L

LD L,A

LD (RND+1),HL




;rnd from Dave Perry's games

PUSH HL

PUSH HL

LD DE,(SEED)

LD H, E

LD L, #FD

LD A, D

AND A

SBC HL, DE

SBC A,0

SBC HL, DE

SBC A,0

LD E,A

LD D,0

SBC HL, DE

JR NC,$+3

INC HL

LD (SEED),HL

POP DE

POP HL

RET

SEED DW 0

А и B SEED останется RND



;from EXOLON by Rafaelle Cecco

;return A-random number

RND push hl

push de

push bc

ld hl, (rndSEED)

ld de, 7

add hl, de

ld e, l

ld d, h

add hl, hl

add hl, hl

ld c, l

ld b, h

add hl, hl

add hl, bc

add hl, de

ld (rndSEED), hl

xor h

pop bc

pop de

pop hl

ret

rndSEED dw 8EAAh





;from Lop Ears game by Players

 RANDOM LD DE,(SEED)

LD H,E

LD L,253

LD A,D

OR A

LD B,0

SBC HL,DE

  SBC A,B

  SBC HL,DE

  SBC A,B

  LD E,A

LD D,B

SBC HL,DE

  JP NC,RANDOM2

  INC HL

RANDOM2 LD (SEED),HL

  RET

HEX, DEC, BIN, ASCII...

;a=0-3 bit of hex

or #f0

daa

add a,#a0

adc a,#40

;out A=ascii code of hex digit to print


;a=0-#0f

add a,#90

daa

adc a,#40

daa

;out A=ascii code of hex digit to print





;зажигает нужный бит в 16-разрядном регистре by Destr

;например:

;На входе A = 5

;На выходе HL = %0000000000100000

; SET A,HL

LD HL,0

RLA

RLA

CP #20

RLA

OR #C4

LD ($+4),A

DW #CBCB


;Или чуть медленней, но уже с игнором старшего полубайта А

; SET A,HL

LD HL,0

SLI A

SLA A

CP #20

RLA

OR #C4

LD ($+4),A

DW #CBCB




;from Fast Tracker

Decimal65535max ex de,hl

push hl

ld l,c

ld h,b

ld bc,#2710 ;10000

call decdigg

jr hd4096

Decimal4096max EX  DE, HL

PUSH HL

LD L, C

LD H, B

hd4096 LD BC, #3E8 ;1000

CALL decdigg

LD BC, #64 ;100

CALL decdigg

LD BC, #A ;10

CALL decdigg

LD A, L

ADD A, #30

LD (DE), A

INC DE

POP HL

EX  DE, HL

RET

decdigg LD A, #2F

OR  A

decdigj INC A

SBC HL, BC

JR NC, decdigj

ADD HL, BC

LD (DE), A

INC DE

RET






;hex to Ascii из Fast Tracker

HexDigitToAsciiCode_X0 RRCA

RRCA

RRCA

RRCA

HexDigitToAsciiCode_0X AND #F

ADD A,#90;hex to ascii

DAA 

ADC A,#40

DAA 

CP #30 ;если ascii код это Ноль

RET NZ ;то

LD A,".";печатаем точки

RET






Библиотечка печати в различных системах счисления.

;------ PDES LIB ------

;Printing in Different Evaluation Systems LIBrary

;(c) SerzhSoft, Shadrinsk, V,3 1997

_NULL   EQU     0   ;используется в изменяемых командах

;--- print in DEC system ---

PDEC_B  ;Печать десятичного числа в A3 (000..255)

        LD      L,A           ;поместили

        LD      H,#00         ; число в HL

        LD      DE,DECTB_W+4  ;адрес в таблице, начиная с сотни

        LD      B,#03         ;макс. возможное кол-во цифр - три

        JR      LP_PDW1       ;переход на цикл печати

;

PDEC_W  ;Печать десятичного числа в HL3 (00000..65535)

        PUSH    HL            ;закинули печатаемое число на стек

        LD      HL,DECTB_W    ;адрес таблицы степеней десятки

        LD      B,#05         ;макс. возможное количество цифр

LP_PDW1 LD      E,(HL)        ;взяли текущую степень

        INC     HL            ; десятки из таблицы

        LD      D,(HL)        ; и поместили в DE

        INC     HL            ;перешли к след. элементу таблицы

        EX      (SP),HL       ;адрес эл-та <-> печатаемое число

        XOR     A             ;обнулили счетчик и флаг C для SBC

LP_PDW2 INC     A             ;увеличиваем счетчик

        SBC     HL,DE         ;вычитаем текущую степень десятки

        JR      NC,LP_PDW2    ;повторяем пока HL>=0

        ADD     HL,DE         ;HL=HL mod DE; A=HL div DE

        ADD     A,"0"-1       ;перевод A в ASCII-код ("0".."9")

        RST     #10           ;печать десятичной цифры

        EX      (SP),HL       ;HL=адрес эл-та, число -> на стек

        DJNZ    LP_PDW1       ;цикл по цифрам

        POP     HL            ;убрали оставшийся ноль со стека

        RET                   ;выход из процедуры

DECTB_W DW      10000,1000,100,10,1    ;Таблица степеней десятки



;--- print in HEX system ---

PHEX_W  ;Печать шестнадцатеричного числа в HL3 (0000..FFFF)

        LD      A,H           ;печать старшего байта числа

        CALL    PHEX_B        ;вызов процедуры HEX-печати байта

        LD      A,L           ;печать младшего байта числа

PHEX_B  ;Печать шестнадцатеричного числа в A3 (00..FF)

        CALL    PHEX_HB       ;печать по полубайтам

PHEX_HB ;Печать старшего полубайта в A3 (0..F)

        RRCA                  ;меняем

        RRCA                  ; местами

        RRCA                  ; старший

        RRCA                  ; и младший полубайты

PHEX_LB ;Печать младшего полубайта в A3 (0..F)

        PUSH    AF            ;сохранили A на стеке

        AND     #0F           ;отбросили лишние старшие биты

        CP      #0A           ;если A<10,

        JR      C,GO_PHL      ; то перепрыгнули

        ADD     A,"A"-"9"-1   ;корректировка: после "9" идет "A"

GO_PHL  ADD     A,"0"         ;перевод в ASCII-код

        RST     #10           ;печать HEX-цифры ("0".."F")

        POP     AF            ;восстановили A со стека

        RET                   ;выход из процедуры




;--- print in BIN system ---

PBIN_W  ;Печать двоичного числа в HL (16 разрядов 0/1)

        LD      A,H           ;печать старшего байта числа

        CALL    PBIN_B        ;вызов процедуры BIN-печати байта

        LD      A,L           ;печать младшего байта числа

PBIN_B  ;Печать двоичного числа в A (8 разрядов 0/1)

        LD      B,#08         ;в байте - 8 битов

LP_PBB  RLCA                  ;поочередно 'выдвигаем' биты в CF

        PUSH    AF            ;сохранить A на стеке

        LD      A,#18         ;в зависимости от флага C:

        RLA                   ; A="0" или A="1"

        RST     #10           ;печать очередного бита

        POP     AF            ;восстановить A со стека

        DJNZ    LP_PBB        ;поразрядный цикл

        RET                   ;выход из процедуры

;



Listing 1. Процедура печати байта в десятичной системе.

PDEC_B  ;Печать десятичного числа в аккумуляторе3 (000..255)

        LD      HL,DECTB_B    ;адрес таблицы степеней десятки

        LD      B,#03         ;макс. возможное количество цифр

LP_PDB1 LD      C,"0"-1       ;инициализация счетчика

LP_PDB2 INC     C             ;увеличиваем счетчик

        SUB     (HL)          ;вычитаем текущую степень десятки

        JR      NC,LP_PDB2    ;повторять, пока A>=0

        ADD     A,(HL)        ;A=A mod (HL); C=STR$(A div (HL))

        PUSH    AF            ;сохранить A на стеке

        LD      A,C           ;текущая цифра

        RST     #10           ;печать цифры

        POP     AF            ;восстановление аккумулятора

        INC     HL            ;переход к след. степени десятки

        DJNZ    LP_PDB1       ;закрутили цикл по цифрам

        RET                   ;выход из процедуры

DECTB_B DB      100,10,1      ;Таблица степеней3 10-ки для byte





;--------------------------------------------------------------;

;     Printing in Different Evaluation Systems LIBrary v2.0    ;

;          (c) SerzhSoft, Shadrinsk, august, 1997              ;

;--------------------------------------------------------------;

_NULL   EQU     0   ;используется в изменяемых командах

;--------------------------------------------------------------;

;                   print in DEC system:                       ;

;--------------------------------------------------------------;

PDEC_3B ;Печать десятичного числа в AHL (00000000..16777215)

        LD      DE,34464      ;DE=100000-65536

        LD      BC,#FF01      ;B=-1 - счетчик сотен тысяч; C=1

LP_PD31 INC     B             ;увеличиваем счетчик сотен тысяч

        AND     A             ;сбpасываем флаг пеpеноса для SUB

        SBC     HL,DE         ;уменьшаем тpехбайтное число AHL

        SBC     A,C           ; на одну сотню тысяч

        JR      NC,LP_PD31    ;повтоpяем пока нет пеpеполнения

        ADD     HL,DE         ;восстанавливаем положительное

        ADC     A,C           ; значение у AHL; AHL<100000

        PUSH    AF            ;сохpаняем на стеке нужные нам

        PUSH    HL            ; впоследствии pегистpы A, HL

        LD      A,B           ;количество сотен тысяч в числе

        CALL    PDEC_B        ;печатаем это значение

        POP     HL            ;восстанавливаем со стека

        POP     AF            ; pегистpы A, HL

        DEC     A             ;если остаток<65536,

        JR      NZ,PDEC_W     ; то пеpеходим на печать 0..65535

        LD      DE,5536       ;иначе - число 65536..99999

        ADD     HL,DE         ;пpибавляем остаток от десятков

        ADC     A,6           ; тысяч, и их сами - отдельно

        PUSH    HL            ;---------------------------------

        LD      HL,DECTB_W    ; Обходимый фpагмент пp-pы PDEC_W

        LD      B,#05         ;---------------------------------

        JR      LP_PDW1+1     ;пpыжок чеpез обнуление дес. тысяч

;--------------------------------------------------------------;

PDEC_B  ;Печать десятичного числа в A (000..255)

        LD      L,A           ;поместили

        LD      H,#00         ; число в HL

        PUSH    HL            ;закинули печатаемое число на стек

        LD      HL,DECTB_W+4  ;адрес в таблице, начиная с сотни

        LD      B,#03         ;макс. возможное кол-во цифр - три

        JR      LP_PDW1       ;переход на цикл печати

;--------------------------------------------------------------;

PDEC_W  ;Печать десятичного числа в HL (00000..65535)

        PUSH    HL            ;закинули печатаемое число на стек

        LD      HL,DECTB_W    ;адрес таблицы степеней десятки

        LD      B,#05         ;макс. возможное количество цифр

LP_PDW1 XOR     A             ;обнулили счетчик и флаг C для SBC

        LD      E,(HL)        ;взяли текущую степень

        INC     HL            ; десятки из таблицы

        LD      D,(HL)        ; и поместили в DE

        INC     HL            ;перешли к след. элементу таблицы

        EX      (SP),HL       ;адрес эл-та <-> печатаемое число

LP_PDW2 INC     A             ;увеличиваем счетчик

        SBC     HL,DE         ;вычитаем текущую степень десятки

        JR      NC,LP_PDW2    ;повторяем пока HL>=0

        ADD     HL,DE         ;HL=HL mod DE; A=HL div DE

        ADD     A,"0"-1       ;перевод A в ASCII-код ("0".."9")

        RST     #10           ;печать десятичной цифры

        EX      (SP),HL       ;HL=адрес эл-та, число -> на стек

        DJNZ    LP_PDW1       ;цикл по цифрам

        POP     HL            ;убрали оставшийся ноль со стека

        RET                   ;выход из процедуры

;--------------------------------------------------------------;

DECTB_W DW      10000,1000,100,10,1  ;Таблица степеней десятки ;

;--------------------------------------------------------------;

;                   print in HEX system:                       ;

;--------------------------------------------------------------;

PHEX_W  ;Печать шестнадцатеричного числа в HL (0000..FFFF)

        LD      A,H           ;печать старшего байта числа

        CALL    PHEX_B        ;вызов процедуры HEX-печати байта

        LD      A,L           ;печать младшего байта числа

;--------------------------------------------------------------;

PHEX_B  ;Печать шестнадцатеричного числа в A (00..FF)

        CALL    PHEX_HB       ;печать по полубайтам

;--------------------------------------------------------------;

PHEX_HB ;Печать старшего полубайта в A (0..F)

        RRCA                  ;меняем

        RRCA                  ; местами

        RRCA                  ; старший

        RRCA                  ; и младший полубайты

;--------------------------------------------------------------;

PHEX_LB ;Печать младшего полубайта в A (0..F)

        PUSH    AF            ;сохранили A на стеке

        AND     #0F           ;отбросили лишние старшие биты

        CP      #0A           ;если A<10,

        JR      C,GO_PHL      ; то перепрыгнули

        ADD     A,"A"-"9"-1   ;корректировка: после "9" идет "A"

GO_PHL  ADD     A,"0"         ;перевод в ASCII-код

        RST     #10           ;печать HEX-цифры ("0".."F")

        POP     AF            ;восстановили A со стека

        RET                   ;выход из процедуры

;--------------------------------------------------------------;

;                    print in BIN system:                      ;

;--------------------------------------------------------------;

PBIN_W  ;Печать двоичного числа в HL (16 разрядов 0/1)

        LD      A,H           ;печать старшего байта числа

        CALL    PBIN_B        ;вызов процедуры BIN-печати байта

        LD      A,L           ;печать младшего байта числа

;--------------------------------------------------------------;

PBIN_B  ;Печать двоичного числа в A (8 разрядов 0/1)

        LD      B,#08         ;в байте - 8 битов

LP_PBB  RLCA                  ;поочередно 'выдвигаем' биты в CF

        PUSH    AF            ;сохранить A на стеке

        LD      A,#18         ;в зависимости от флага C:

        RLA                   ; A="0" или A="1"

        RST     #10           ;печать очередного бита

        POP     AF            ;восстановить A со стека

        DJNZ    LP_PBB        ;поразрядный цикл

        RET                   ;выход из процедуры

;--------------------------------------------------------------;

;                   print in USER system:                      ;

;--------------------------------------------------------------;

PUSE_W  ;Печать числа в установленной MKUSETB системе сч-я в HL

        PUSH    HL            ;закинули печатаемое число на стек

HL_PUW  LD      HL,_NULL      ;адрес конца таблицы степеней

LP_PUW1 DEC     HL            ;загружаем в DE очередную

        LD      D,(HL)        ; степень числа

        DEC     HL            ; из таблицы,

        LD      E,(HL)        ; двигаясь 'сверху-вниз'

        EX      (SP),HL       ;адрес таблицы <-> печат-е число

        XOR     A             ;обнуление A и сброс флага C

LP_PUW2 INC     A             ;вычисляем очередной

        SBC     HL,DE         ; разряд числа и результат

        JR      NC,LP_PUW2    ; помещаем в A (A=1+(HL div DE))

        ADD     HL,DE         ;восстанавливаем (+) значение

        ADD     A,"0"-1       ;переводим A в ASCII-код

        CP      "9"+1         ;если код меньше "9",

        JR      C,GO_PUW1     ; то переход на печать

        ADD     A,"A"-"9"-1   ;коррекция (после "9" идет "A")

GO_PUW1 RST     #10           ;печать очередной цифры числа

        EX      (SP),HL       ;HL=адрес таблицы, число -> стек

        DEC     DE            ;если степень

        LD      A,D           ; числа не равна единице

        OR      E             ; (самый первый элемент таблицы),

        JR      NZ,LP_PUW1    ; то продолжаем работу

        POP     HL            ;убираем со стека ненужный 0

        RET                   ;выход из процедуры

;--------------------------------------------------------------;

MKUSETB ;Создание таблицы степеней с основанием в A

        LD      HL,USE_TBL    ;адрес создаваемой таблицы

        LD      DE,#0001      ;инициализация счетчика степени

LP_MUT1 LD      (HL),E        ;запись текущего

        INC     HL            ; значения степени

        LD      (HL),D        ; в таблицу и переход

        INC     HL            ; к ее следующей ячейке

        PUSH    HL            ;сохраняем адрес на стеке

        LD      B,A           ;осн-е степени -> в счетчик цикла

        LD      HL,#0000      ;обнуление результата

LP_MUT2 ADD     HL,DE         ;подсчитываем результат

        JR      C,GO_MUT      ;если HL>65535, то прерываем счет

        DJNZ    LP_MUT2       ;повторяем <основание> раз

        EX      DE,HL         ;результат -> в DE (новая степень)

GO_MUT  POP     HL            ;восстанавливаем адрес таблицы

        JR      NC,LP_MUT1    ;если не прерывались, то повторяем

        LD      (HL_PUW+1),HL ;адрес конца таблицы -> в PUSE_W

        RET                   ;выход из процедуры

;--------------------------------------------------------------;

USE_TBL DS      32   ;Таблица степеней текущей системы счисления

;--------------------------------------------------------------;

;                    print in RIM system:                      ;

;--------------------------------------------------------------;

PRIM_B  ;Печать числа в римской записи в A (I..CCLV)

        LD      L,A           ;скопировать

        LD      H,#00         ; A в HL

;--------------------------------------------------------------;

PRIM_W  ;Печать числа в римской записи в HL (I..MMMCMXCIX)

        PUSH    HL            ;закинули печатаемое число на стек

        LD      HL,RIM_TBL    ;адрес таблицы знаков

        DB      #DD           ;работаем с половинкой регистра IX

        LD      L,#07         ;LD XL,число знаков в р. счислении

LP_PRW1 LD      E,(HL)        ;считываем из таблицы значение

        INC     HL            ; очередного знака римской системы

        LD      D,(HL)        ; счисления и помещаем в DE

        INC     HL            ;затем в регистр C считываем

        LD      C,(HL)        ; ASCII-код знака

        INC     HL            ;переход к следующему знаку

        LD      (HL_PRW+1),HL ;сохранили адрес следующего знака

        EX      (SP),HL       ;адрес -> на стек, HL=печат. число

        XOR     A             ;обнуление счетчика и сброс CF

LP_PRW2 INC     A             ;в цикле производим

        SBC     HL,DE         ; деление HL на DE (вычитаем,

        JR      NC,LP_PRW2    ; пока нет переполнения)

        ADD     HL,DE         ;восстанавливаем (+) значение

        DEC     A             ;т.к. A на 1 больше  HL div DE,

        JR      Z,GO_PRW1     ; то если A=1 - ничего не печатаем

        LD      B,A           ;количество печатаемых символов

LP_PRW3 LD      A,C           ;код знака

        RST     #10           ;печатаем его

        DJNZ    LP_PRW3       ;сколько надо - столько и печатаем

GO_PRW1 DB      #DD           ;работаем с половинкой регистра IX

        LD      A,L           ;LD A,XL - номер текущего знака

        DEC     A             ;если это последний знак (I), то

        JR      Z,GO_PRW4     ; двухбуквенного сочетания нет

        EX      (SP),HL       ;HL=адрес след. знака, число->стек

        RRA                   ;если номер текущего знака четный,

        JR      C,GO_PRW2     ; то сочетание с следующим знаком

        INC     HL            ;иначе - перепрыгнуть через один

        INC     HL            ; знак. В результате получим адрес

        INC     HL            ; знака, для получения двойного

GO_PRW2 LD      A,C           ; знакосочетания (IV,CM,XL...)

        LD      C,(HL)        ;взяли из таблицы значение для

        INC     HL            ; этого знака и получили

        LD      B,(HL)        ; разность между значением

        INC     HL            ; основного и этим значением.

        EX      DE,HL         ; Например, для основного знака X

        AND     A             ; дополнительным будет I, а их

        SBC     HL,BC         ; разность соответственно:

        EX      DE,HL         ; 10-1=9, т.е. IX

        LD      C,A           ;код основного знака

        LD      A,(HL)        ;код дополнительного знака

        EX      (SP),HL       ;HL=печатаемое число

        SBC     HL,DE         ;если оно < дополнительного

        JR      C,GO_PRW3     ; значения, то не печатаем

        RST     #10           ;печать дополнительного знака

        LD      A,C           ;основной знак

        RST     #10           ;печать

        JR      GO_PRW4       ;результат - двухбукв. сочетание

GO_PRW3 ADD     HL,DE         ;восстановили (+) у числа

GO_PRW4 EX      (SP),HL       ;число -> на стек

HL_PRW  LD      HL,_NULL      ;адрес следующего знака в таблице

        DB      #DD           ;работаем с половинкой регистра IX

        DEC     L             ;DEC XL - уменьшаем счетчик знаков

        JR      NZ,LP_PRW1    ;крутим цикл, пока есть еще знаки

        POP     HL            ;сняли ненужный уже ноль со стека

        RET                   ;выход из процедуры

;--------------------------------------------------------------;

RIM_TBL ;Таблица значимости букв в написании римских чисел

        DW      1000

        DB      "M"           ;M=1000 ;CM=900

        DW      500

        DB      "D"           ;D=500  ;CD=400

        DW      100

        DB      "C"           ;C=100  ;XC=90

        DW      50

        DB      "L"           ;L=50   ;XL=40

        DW      10

        DB      "X"           ;X=10   ;IX=9

        DW      5

        DB      "V"           ;V=5    ;IV=4

        DW      1

        DB      "I"           ;I=1    ;-nop-

;--------------------------------------------------------------;

;                  end of PDES LIB 2.0                         ;

;--------------------------------------------------------------;







Эта программа распечатывает двухбайтовое

шестнадцатиричное

число   (#0000...#FFFF),  

которое задается в регистре HL на входе в

процедуру. Печать выполняется при 

помощи RST 16,  поэтому перед  ее

использованием должен быть открыт 

канал печати

(для экрана: LD A,2:CALL #1601)

PRH LD   C,#04       

PH0 LD   B,#04       

XOR  A           

PH1 RL   L           

RL   H           

RLA              

DJNZ PH1         

CP   #0A         

JR   C,PH2       

ADD  A,#07       

PH2    ADD  A,#30       

RST  16          

DEC  C           

JR   NZ,PH0      

RET     



As this is one of the most typical tasks, 

why not to do it tricky way? 

The code snippet here takes a byte from (HL) and prints it. 

Note that it uses another (shorter) DAA trick as we know that 

Half Carry is cleared before DAA. 

Input: HL = address of data ; Output: memory dump 

Note: You'll have to replace the PRINT_CHAR macro by actual 

platform-specific code. 

Don't forget to preserve the contents of HL! 

xor a

rld

call Nibble

Nibble push af

daa

add a,F0h

adc a,40h


PRINT_CHAR ; prints ASCII character in A


pop af

rld

ret






; Convert single byte in A to two Ascii characters in B and C

TO_HEX  LD   B,A                ; Save the byte original value

AND  $0F        ; Strip off the high order nybble

CP   $0A        ; 0-9 at this point?

JR   C,TOHX01   ; We only need to add $30 then, or

ADD  A,$07      ; Seven more if it's in the A-F range

TOHX01  ADD  A,$30              ; Take the value on up to ASCII

LD   C,A        ; Store the converted lo order character to C

LD   A,B        ; Get the original value back

RRCA            ; Rotate it to the right

RRCA

RRCA

RRCA

AND  $0F        ; Mask off the upper trash

CP   $0A        ; 0-9 (< $0A)?

JR   C,TOHX02   ; Only add $30 then

ADD  A,$07      ; And add it 7 if it's in the A-F range

TOHX02  ADD  A,$30              ; Finish the conversion to ASCII

LD   B,A              ; Load the finished hi order character to B

RET





;Number in hl to decimal ASCII

;Thanks to z80 Bits

;inputs: hl = number to ASCII

;example: hl=300 outputs '00300'

;destroys: af, bc, hl, de used

DispHL ld bc,-10000

call Num1

ld bc,-1000

call Num1

ld bc,-100

call Num1

ld c,-10

call Num1

ld c,-1

Num1: ld a,'0'-1

Num2: inc a

add hl,bc

jr c,Num2

sbc hl,bc

call prt

ret 

;adaptable for a DispA routine 

or any other 8-bit register

DispA ld h,0

ld l,a

jp DispHL




;Display a 16- or 8-bit number in hex.

DispHLhex:

; Input: HL

   ld  c,h

   call  OutHex8

   ld  c,l

OutHex8:

; Input: C

   ld  a,c

   rra

   rra

   rra

   rra

   call  Conv

   ld  a,c

Conv:

   and  $0F

   add  a,$90

   daa

   adc  a,$40

   daa

   call prt

   ret

 







;ASCII to DEC

;HL-адрес числа

;Выход:DE-число

        LD HL,TXT

        CALL MakeNumber

        RET 

MakeNumber

        LD DE,0

MkNum1  LD A,(HL)

        CALL isdigit

        RET NC

        EX DE,HL

        ADD HL,HL

        LD B,H

        LD C,L

        ADD HL,HL

        ADD HL,HL

        ADD HL,BC

        LD B,0

        SUB "0"

        LD C,A

        ADD HL,BC

        EX DE,HL

        INC HL

        JR MkNum1

isdigit CP      "0"

        CCF 

        RET     NC

        CP      "9"+1

        RET 

TXT     DB "768QWEIUSO"




Hexadecimal conversion operates directly 

on nibbles and takes advantage of nifty DAA trick. 

Input: 

HL = number to convert, 

DE = location of ASCII string 

Output: 

ASCII string at (DE) 

Num2Hex ld a,h

call Num1

ld a,h

call Num2

ld a,l

call Num1

ld a,l

jr Num2

Num1 rra

rra

rra

rra

Num2 or F0h

daa

add a,A0h

adc a,40h

ld (de),a

inc de

ret






NUMBUFF DEFB "00000"

A2DEC8 ld hl,NUMBUFF

ld (hl),#2f

inc (hl)

sub 100

jr nc,$-3

add a,100

inc hl

ld (hl),#2f

inc (hl)

sub 10

jr nc,$-3

add a,10

inc hl

add a,"0"

ld (hl),a

ret





;Write a Hex number in BCHL, CHL, HL or A 

;to memory at DE.

sphex8: ld a,b;Big multidecker fall-through...

call sphex2

sphex6: ld a,c

call sphex2

sphex4: ld      a,h

        call    sphex2

        ld      a,l

sphex2: push    af

        rrca

        rrca

        rrca

        rrca

        call    sphex1

        pop     af

sphex1: and     0fh

        cp      0ah

        jr      c,sph1a

        add     a,7

sph1a:  add     a,'0'

        ld      (de),a

        inc     de

        ret







;Write decimal numbers in HL to memory at DE

cphlbc: push hl

and a

sbc hl,bc

pop hl

ret

spdec: ld bc,10000

call cphlbc

jr nc,spdec5

ld bc,1000

call cphlbc

jr nc,spdec4

ld bc,100

call cphlbc

jr nc,spdec3

ld a,l

cp 10

jr nc,spdec2 ;>=10

jr spdec1

spdec5: ld bc,10000

call dodec

spdec4: ld bc,1000

call dodec

spdec3: ld bc,100

call dodec

spdec2: ld bc,10

call dodec

spdec1: ld a,l

add a,'0'

ld (de),a

inc de

ret

dodec: ld a,'0'

ddlp: inc a

and a

sbc hl,bc

jr nc,ddlp

dec a

add hl,bc

ld (de),a

inc de

ret

;






;by GRAND, 1999 

;Печ.числа (0...65535) 

;с незн нулями 

;Вх.: HL - число.

PR16D XOR A

  LD DE,10000

  SBC HL,DE

  INC A

  JR NC,$-3

  ADD A,"0"-1

CALL PRINTA

  ADD HL,DE

  XOR A

LD DE,1000

SBC HL,DE

  INC A

  JR NC,$-3

  ADD A,"0"-1

  CALL PRINTA

  ADD HL,DE

;Печ.числа (0...255) с незн нулями

;Вх.: HL - число.

PR8D XOR A

LD DE,100

SBC HL,DE

INC A

JR NC,$-3

ADD A,"0"-1

CALL PRINTA

ADD HL,DE

;Печ.числа (0...99) с незн нулями

;Вх.: HL - число.

LD A,L

LD B,"0"-1

INC B

SUB 10

JR NC,$-3

ADD A,"0"+10

LD C,A

LD A,B

CALL PRINTA

LD A,C

PRINTA.....print proc here




; Routine for converting a 24-bit binary number to decimal

; In: E:HL = 24-bit binary number (0-16777215)

; Out: DE:HL = 8 digit decimal form (packed BCD)

; Changes: AF, BC, DE, HL & IX

; by Alwin Henseler

                 LD C,E

                 PUSH HL

                 POP IX          ; input value in C:IX

                 LD HL,1

                 LD D,H

                 LD E,H          ; start value corresponding with 1st 1-bit

                 LD B,24         ; bitnr. being processed + 1

FIND1:           ADD IX,IX

                 RL C            ; shift bit 23-0 from C:IX into carry

                 JR C,NEXTBIT

                 DJNZ FIND1      ; find highest 1-bit

; All bits 0:

                 RES 0,L         ; least significant bit not 1 after all ..

                 RET

DBLLOOP:         LD A,L

                 ADD A,A

                 DAA

                 LD L,A

                 LD A,H

                 ADC A,A

                 DAA

                 LD H,A

                 LD A,E

                 ADC A,A

                 DAA

                 LD E,A

                 LD A,D

                 ADC A,A

                 DAA

                 LD D,A          ; double the value found so far

                 ADD IX,IX

                 RL C            ; shift next bit from C:IX into carry

                 JR NC,NEXTBIT   ; bit = 0 -> don't add 1 to the number

                 SET 0,L         ; bit = 1 -> add 1 to the number

NEXTBIT:         DJNZ DBLLOOP

                 RET




ПРЕОБРАЗОВАНИЕ ДВОИЧНОГО ЧИСЛА В ДВОИЧНО-ДЕСЯТИЧНОЕ.

   Для  отображения  результатов вычислений   необходимо числа

привести в  удобный  для  вывода вид, т.е. преобразовать число  в

десятичный код. Например, двоичное число 00001111 (#0F)  удобно

представить  в  виде  0001  0101(#15), т.е. преобразовать его  в

двоично-десятичную  форму.  Этим занимается следующая подпрограмма.

; BCD2B- подпрограмма перевода 2-х байтного числа.

; Двоичное число должно быть в регистре 'HL'.

; Результат :

;           в 'A' - десятки тысяч;

;           в 'B' - тысячи и сотни;

;           в 'C' - десятки и единицы.

; Во время работы вызывается процедура CONV из

; подпрограммы BCD1B - перевод однобайтного числа,

; в которой;

; в 'H' - число для перевода , 'L'=0

; Результат работы: в 'A' - разряды сотен;

;                   в 'B' - десятки и единицы

; Вход в процедуру преобразования 2-х байтного числа:

BCD2B   LD E,17     ; счетчик 1-го цикла

        CALL CONV   ; вычислить младший BCD байт

        LD C,A      ; сохранить в 'C'

        LD E,17     ; счетчик 2-го цикла

        JR PRODOL   ; переход на вычисление

; процедура для преобразования 1-байтного числа

BCD1B   LD E,9      ; цикл для 1-байтного числа

PRODOL  CALL CONV   ; вычислить два старших байта

        LD B,A      ; сохранить средний байт

        LD A,L      ; старший байт

        RET         ; выйти вообще

;-----

CONV    XOR A       ; очистить

SBIT    DEC E       ; уменьшим счетчик цикла

        RET Z       ; цикл весь - выйти

        ADD HL,HL   ; сдвинем старшие разряды в перенос

        ADC A,A

        DAA         ; скорректируем

        JR NC,SBIT  ; результат больше 99 ?

        INC HL      ; да - увеличим на 1

        JR SBIT     ; вернемся в вычисления




from PENTIS by Tony Raugas print DEC

print_digits    exx

                ld      hl, txt_00000

                ld      b, 5

prd1            ld      (hl), #30

                inc     hl

                djnz    prd1

                exx

                ld      b, #10

prd2            exx

                ld      hl,  txt_00000+4

                ld      bc, #500

prd3            ld      a, (hl)

                add     a, a

                add     a, c

                sub     #30 

                cp      #3A 

                jr      c, prd4

                sub     #A

                ld      c, 1

                jr      prd5

prd4            ld      c, 0

prd5            ld      (hl), a

                dec     hl

                djnz    prd3

                exx

                add     hl, hl

                exx

                jr      nc, prd7

                ld      hl,  txt_00000+4

                ld      b, 5

prd6            ld      a, (hl)

                inc     a

                ld      (hl), a

                cp      #3A

                jr      c, prd7

                ld      (hl), #30

                dec     hl

                djnz    prd6

prd7            exx

                djnz    prd2

                ld      hl, txt_00000

jp prtpz

txt_00000       db "00000",0





Печать десятичных чисел без нуля. 59 байт. print DEC w/o zeros

        LD IX,Буфер 5 байт

        LD HL,Число

        LD BC,0

        LD DE,0-10000

        CALL num

        LD DE,0-1000

        CALL num

        LD DE,0-100

        CALL num

        LD DE,0-10

        CALL num

        LD A,L

        JR num2

num       ADD HL,DE

        INC B

        JR C,$-2

        DEC B

        SBC HL,DE

        LD A,B

        OR C

        RET Z

        LD C,A

        LD A,B

num2    ADD A,48

        LD (IX),A

        INC IX

        LD B,0

        RET





CISLO           ld      de,-10000       ;rad desetitisicu

                call    CIFRA

                ld      de,-1000        ;rad tisicu

                call    CIFRA

                ld      de,-100         ;rad stovek

                call    CIFRA

                ld      de,-10          ;rad desitek

                call    CIFRA

                ld      de,-1           ;rad jednicek

CIFRA           ld      a,'0'-1         ;do A znak 0 zmenseny o 1

CIFRA1          add     hl,de           ;pricti zaporny rad

                inc     a               ;zvys pocitadlo

                jr      c,CIFRA1        ;jeste jsme jej neprekrocili

                sbc     hl,de           ;pokud ano, oprav cislo

          ;      call    ZNAK            ;a vytiskni pocitadlo

                ret






NUM2DEC:        ld      e,' '

                ld      bc,-10000

                call    NUM1

                ld      bc,-1000

                call    NUM1

                ld      bc,-100

                call    NUM1

                ld      c,-10

                call    NUM1

                ld      e,'0'

                ld      c,b

NUM1:           ld      a,'0'-1

NUM2:           inc     a

                add     hl,bc

                jr      c,NUM2

                sbc     hl,bc

                cp      '0'

                jr      nz,NUM4

                ld      a,e

NUM3:           jp      ZNAK2

NUM4:           ld      e,'0'

                jr      NUM3




Input: HL = number to convert, DE = location of ASCII string

Output: ASCII string at (DE) ; Num2Dec ld bc,-10000

call Num1

ld bc,-1000

call Num1

ld bc,-100

call Num1

ld c,-10

call Num1

ld c,b

Num1 ld a,'0'-1

Num2 inc a

add hl,bc

jr c,Num2

sbc hl,bc

ld (de),a

inc de

ret







;in: HL

DECWORD LD      IY,DECWTAB

        XOR     A

        LD      B,4

DECW3   LD      E,(IY)

        INC     IY

        LD      D,(IY)

        INC     IY

DECW2   OR      A

        SBC     HL,DE

        JR      C,DECW1

        INC     A

        JR      DECW2

DECW1   ADD     HL,DE

        CALL    DECWPC

        DJNZ    DECW3

        LD      A,L

        OR      A,#30

        JR      PRCHAR

DECWPC  OR      A

        RET     Z

        PUSH    HL

        PUSH    BC

        OR      #30

        CALL    PRCHAR

        LD      A,#30

        POP     BC

        POP     HL

        RET

DECWTAB DEFW    10000,1000,100,10



;in:    HL

HEXWORD PUSH    HL

        LD      A,H

        CALL    HEXBYTE

        POP     HL

        LD      A,L

;in:    A

HEXBYTE PUSH    AF

        RRCA

        RRCA

        RRCA

        RRCA

        CALL    HEXHALF

        POP     AF

HEXHALF AND     #0F

        CP      #0A

        JR      C,HEXH1

        ADD     A,#07

HEXH1   ADD     A,#30

;  CALL PRCHAR




; Convert HEX ASCII characters in B C registers to a byte value in A

HEX2BIN LD   A,B                ; Move the hi order byte to A

SUB  $30                ; Take it down from Ascii

CP   $0A                ; Are we in the 0-9 range here?

JR   C,HX2B01           ; If so, get the next nybble

SUB  $07                ; But if A-F, take it down some more

HX2B01  RLCA                    ; Rotate the nybble from low to high

RLCA                    ; One bit at a time

RLCA                    ; Until we

RLCA                    ; Get there with it

LD   B,A                ; Save the converted high nybble

LD   A,C                ; Now get the low order byte

SUB  $30                ; Convert it down from Ascii

CP   $0A                ; 0-9 at this point?

JR   C,HX2B02           ; Good enough then, but

SUB  $07                ; Take off 7 more if it's A-F

HX2B02  ADD  A,B                ; Add in the high order nybble

RET


; Convert single byte in A to two Ascii characters in B and C

TO_HEX  LD   B,A                ; Save the byte original value

AND  $0F                ; Strip off the high order nybble

CP   $0A                ; 0-9 at this point?

JR   C,TOHX01           ; We only need to add $30 then, or

ADD  A,$07              ; Seven more if it's in the A-F range

TOHX01  ADD  A,$30              ; Take the value on up to ASCII

LD   C,A                ; Store the converted lo order character to C

LD   A,B                ; Get the original value back

RRCA                    ; Rotate it to the right

RRCA

RRCA

RRCA

AND  $0F                ; Mask off the upper trash

CP   $0A                ; 0-9 (< $0A)?

JR   C,TOHX02           ; Only add $30 then

ADD  A,$07              ; And add it 7 if it's in the A-F range

TOHX02  ADD  A,$30              ; Finish the conversion to ASCII

LD   B,A                ; Load the finished hi order character to B

RET                     ;





;CLCN32 -> Converts 32Bit-Value in ASCII-String (terminated by 0)

;Input      DE,IX=32bit value, IY=destination address

;Output     IY=last char in destination string

;Destroyed AF,BC,DE,HL,IX

clcn32t dw 1,0,     10,0,     100,0,     1000,0,     10000,0

        dw #86a0,1, #4240,#f, #9680,#98, #e100,#5f5, #ca00,#3b9a

clcn32z ds 4

clcn32  ld (clcn32z),ix

        ld (clcn32z+2),de

        ld ix,clcn32t+36

        ld b,9

        ld c,0

clcn321 ld a,"0"

        or a

clcn322 ld e,(ix+0):ld d,(ix+1):ld hl,(clcn32z):  sbc hl,de:ld (clcn32z),hl

        ld e,(ix+2):ld d,(ix+3):ld hl,(clcn32z+2):sbc hl,de:ld (clcn32z+2),hl

        jr c,clcn325

        inc c

        inc a

        jr clcn322

clcn325 ld e,(ix+0):ld d,(ix+1):ld hl,(clcn32z):  add hl,de:ld (clcn32z),hl

        ld e,(ix+2):ld d,(ix+3):ld hl,(clcn32z+2):adc hl,de:ld (clcn32z+2),hl

        ld de,-4

        add ix,de

        inc c

        dec c

        jr z,clcn323

        ld (iy+0),a

        inc iy

clcn323 djnz clcn321

        ld a,(clcn32z)

        add "0"

        ld (iy+0),a

        ld (iy+1),0

        ret





; Combined routine for conversion of different sized binary numbers into

; directly printable ASCII(Z)-string

; Input value in registers, number size and -related to that- registers to fill

; is selected by calling the correct entry:

;  entry  inputregister(s)  decimal value 0 to:

;   B2D8             A                    255  (3 digits)

;   B2D16           HL                  65535   5   "

;   B2D24         E:HL               16777215   8   "

;   B2D32        DE:HL             4294967295  10   "

;   B2D48     BC:DE:HL        281474976710655  15   "

;   B2D64  IX:BC:DE:HL   18446744073709551615  20   "

; The resulting string is placed into a small buffer attached to this routine,

; this buffer needs no initialization and can be modified as desired.

; The number is aligned to the right, and leading 0's are replaced with spaces.

; On exit HL points to the first digit, (B)C = number of decimals

; This way any re-alignment / postprocessing is made easy.

; Changes: AF,BC,DE,HL,IX

; P.S. some examples below

; by Alwin Henseler

B2D8:    LD H,0

         LD L,A

B2D16:   LD E,0

B2D24:   LD D,0

B2D32:   LD BC,0

B2D48:   LD IX,0          ; zero all non-used bits

B2D64:   LD (B2DINV),HL

         LD (B2DINV+2),DE

         LD (B2DINV+4),BC

         LD (B2DINV+6),IX ; place full 64-bit input value in buffer

         LD HL,B2DBUF

         LD DE,B2DBUF+1

         LD (HL)," "

B2DFILC: EQU $-1         ; address of fill-character

         LD BC,18

         LDIR            ; fill 1st 19 bytes of buffer with spaces

         LD (B2DEND-1),BC ;set BCD value to "0" & place terminating 0

         LD E,1          ; no. of bytes in BCD value

         LD HL,B2DINV+8  ; (address MSB input)+1

         LD BC,#0909

         XOR A

B2DSKP0: DEC B

         JR Z,B2DSIZ     ; all 0: continue with postprocessing

         DEC HL

         OR (HL)         ; find first byte <>0

         JR Z,B2DSKP0

B2DFND1: DEC C

         RLA

         JR NC,B2DFND1   ; determine no. of most significant 1-bit

         RRA

         LD D,A          ; byte from binary input value

B2DLUS2: PUSH HL

         PUSH BC

B2DLUS1: LD HL,B2DEND-1  ; address LSB of BCD value

         LD B,E          ; current length of BCD value in bytes

         RL D            ; highest bit from input value -> carry

B2DLUS0: LD A,(HL)

         ADC A,A

         DAA

         LD (HL),A       ; double 1 BCD byte from intermediate result

         DEC HL

         DJNZ B2DLUS0    ; and go on to double entire BCD value (+carry!)

         JR NC,B2DNXT

         INC E           ; carry at MSB -> BCD value grew 1 byte larger

         LD (HL),1       ; initialize new MSB of BCD value

B2DNXT:  DEC C

         JR NZ,B2DLUS1   ; repeat for remaining bits from 1 input byte

         POP BC          ; no. of remaining bytes in input value

         LD C,8          ; reset bit-counter

         POP HL          ; pointer to byte from input value

         DEC HL

         LD D,(HL)       ; get next group of 8 bits

         DJNZ B2DLUS2    ; and repeat until last byte from input value

B2DSIZ:  LD HL,B2DEND    ; address of terminating 0

         LD C,E          ; size of BCD value in bytes

         OR A

         SBC HL,BC       ; calculate address of MSB BCD

         LD D,H

         LD E,L

         SBC HL,BC

         EX DE,HL        ; HL=address BCD value, DE=start of decimal value

         LD B,C          ; no. of bytes BCD

         SLA C           ; no. of bytes decimal (possibly 1 too high)

         LD A,"0"

         RLD             ; shift bits 4-7 of (HL) into bit 0-3 of A

         CP "0"          ; (HL) was > 9h?

         JR NZ,B2DEXPH   ; if yes, start with recording high digit

         DEC C           ; correct number of decimals

         INC DE          ; correct start address

         JR B2DEXPL      ; continue with converting low digit

B2DEXP:  RLD             ; shift high digit (HL) into low digit of A

B2DEXPH: LD (DE),A       ; record resulting ASCII-code

         INC DE

B2DEXPL: RLD

         LD (DE),A

         INC DE

         INC HL          ; next BCD-byte

         DJNZ B2DEXP     ; and go on to convert each BCD-byte into 2 ASCII

         SBC HL,BC       ; return with HL pointing to 1st decimal

         RET

B2DINV:  DS 8            ; space for 64-bit input value (LSB first)

B2DBUF:  DS 20           ; space for 20 decimal digits

B2DEND:  DS 1            ; space for terminating 0

         END


     EXAMPLES

(In these examples, it is assumed there exists a subroutine PRINT, that

prints a string (terminated by a 0-byte) starting at address [HL] )

Print 1 byte, as follows:

 20

  7

145  etc.

by:   LD A,byte

      CALL B2D8

      LD HL,B2DEND-3

      CALL PRINT

Print a 24-bit value, as follows:

9345

76856366

534331

by:   LD E,bit23-16

      LD HL,bit15-0

      CALL B2D24

      CALL PRINT

Print a 48-bit value, like

    14984366484

             49

123456789012345

        3155556 etc.

by:

      LD BC,bit47-32

      LD DE,bit31-16

      LD HL,bit15-0

      CALL B2D48

      LD HL,B2DEND-15

      CALL PRINT

....... ETC , ETC , ETC , ...... 






;Converts a single ASCII hex char to a nybble value

;Pass char in reg A. Letter numerals must be upper case.

;Return nybble value in low-order reg A with zeros in high-order nybble if no error.

;Return 0ffh in reg A if error (char not a valid hex numeral).

;Also uses b, c, and hl registers.

hex_char_to_nybble: ld hl,hex_char_table

ld b,00fh ;no. of valid characters in table - 1.

ld c,000h ;will be nybble value

hex_to_nybble_loop: cp (hl) ;character match here?

jp z,hex_to_nybble_ok ;match found, exit

dec b ;no match, check if at end of table

jp m,hex_to_nybble_err ;table limit exceded, exit with error

inc c ;still inside table, continue search

inc hl

jp hex_to_nybble_loop

hex_to_nybble_ok: ld a,c ;put nybble value in a

ret

hex_to_nybble_err: ld a,0ffh ;error value

ret

;

;Converts a hex character pair to a byte value

;Called with location of high-order char in HL

;If no error carry flag clear, returns with byte value in register A, and

;HL pointing to next mem location after char pair.

;If error (non-hex char) carry flag set, HL pointing to invalid char

hex_to_byte: ld a,(hl) ;location of character pair

push hl ;store hl (hex_char_to_nybble uses it)

call hex_char_to_nybble

pop hl ;returns with nybble value in a reg, or 0ffh if error

cp 0ffh ;non-hex character?

jp z,hex_to_byte_err;yes, exit with error

sla a ;no, move low order nybble to high side

sla a

sla a

sla a

ld d,a ;store high-nybble

inc hl ;get next character of the pair

ld a,(hl)

push hl ;store hl

call hex_char_to_nybble

pop hl

cp 0ffh ;non-hex character?

jp z,hex_to_byte_err;yes, exit with error

or d ;no, combine with high-nybble

inc hl ;point to next memory location after char pair

scf

ccf ;no-error exit (carry = 0)

ret

hex_to_byte_err: scf ;error, carry flag set

ret

hex_char_table: defm "0123456789ABCDEF";ASCII hex table





CRC

;crc16 ccitt на Z80

;hl - from

;b - how many

;de - crc value

;by LVD'2005

crc16:

 ld de,#ffff

loop:

 ld a,d

 ld d,e

 xor (hl)

 inc hl

 ld e,a

 rrca

 rrca

 rrca

 rrca

 and #0f

 xor e

 ld e,a

 rrca

 rrca

 rrca

 ld c,a

 rrca

 and #f0

 xor d

 ld d,a

 ld a,c

 and #1f

 xor d

 ld d,a

 ld a,c

 and #e0

 xor e

 ld e,a

 djnz loop

 ret






16-bit CRC-CCITT

The following routine calculates standard CRC-CCITT bit-by-bit using polynomial 1021h. 

Another common scheme CRC-16 uses polynomial A001h and starts with value 0 

(so it's likely that you misinterpret bunch of zeros as valid data). 

It might be useful to extend the code to use 16-bit byte counter. 

Input: DE = address of input data, C = number of bytes to process ; Output: HL = CRC 

Crc16 ld hl,FFFFh

Read ld a,(de)

inc de

xor h

ld h,a

ld b,8

CrcByte add hl,hl

jr nc,Next

ld a,h

xor 10h

ld h,a

ld a,l

xor 21h

ld l,a

Next djnz CrcByte

dec c

jr nz,Read

ret




from tomdalby

Small project to create Cyclic Redundancy Check for the ZX Spectrum in Z80 machine code.

Started with a simple CRC-8 (9bit) routine using the CCITT 

(Comité Consultatif International Téléphonique et Télégraphique) 

polynominal x8+x2+x+1. For binary data x=2, 

so the polynominal translates to 28+22+2+1 = 256+4+2+1 = 263 which in binary is 00000001 00000111. 

For this implementation the high-order bit (9th bit), which is always 1, 

is omitted so it becomes 00000111, 7 in decimal or 0x07 in hex.

;; =====================================================================

;; input - hl=start of memory to check, de=length of memory to check

;; returns - a=result crc

;; 20b

;; =====================================================================

_crc8b:

xor a ; 4t - initial value of crc=0 so first byte can be XORed in (CCITT)

ld c,$07 ; 7t - c=polyonimal used in loop (small speed up)

_byteloop8b:

xor (hl) ; 7t - xor in next byte, for first pass a=(hl)

inc hl ; 6t - next mem

ld b,8 ; 7t - loop over 8 bits

_rotate8b:

add a,a ; 4t - shift crc left one

jr nc,_nextbit8b ; 12/7t - only xor polyonimal if msb set (carry=1)

xor c ; 4t - CRC8_CCITT = 0x07

_nextbit8b:

djnz _rotate8b ; 13/8t

ld b,a ; 4t - preserve a in b

dec de ; 6t - counter-1

ld a,d ; 4t - check if de=0

or e ; 4t

ld a,b ; 4t - restore a

jr nz,_byteloop8b; 12/7t

ret ; 10t







from tomdalby

CRC-8 is optimal (unique) for a block length of 29-1 or up to 64bytes and 

longer blocks may produce the same CRC value.

Moving to CRC-16 again using CCITT polynominal (as used in Bluetooth) 

x16+x12+x5+1 = 00000001 00010000 00100001 in binary. 

With high-order bit omitted we get 00010000 00100001 or 4129 decimal, 0x1021 hex.

;; =====================================================================

;; input - de=start of memory to check, bc=length of memory to check

;; returns - hl=result crc

;; =====================================================================

_crc16:

ld hl,$ffff ; 10t - initial crc=$ffff

_byte16:

push bc ; 11t - preserve counter

ld a,(de) ; 7t - get byte

inc de ; 6t - next mem

xor h ; 4t - xor byte into crc high byte

ld h,a ; 4t - back into high byte

ld b,8 ; 7t - rotate 8 bits

_rotate16:

add hl,hl ; 11t - rotate crc left one

jr nc,_nextbit16 ; 12/7t - only xor polyonimal if msb set

ld a,h ; 4t

xor $10 ; 7t - high byte with $10

ld h,a ; 4t

ld a,l ; 4t

xor $21 ; 7t - low byte with $21

ld l,a ; 4t - hl now xor $1021

_nextbit16:

djnz _rotate16 ; 13/8t - loop over 8 bits

pop bc ; 10t - bring back main counter

dec bc ; 6t

ld a,b ; 4t

or c ; 4t

jr nz,_byte16 ; 12/7t

ret ; 10t

CRC-16 is optimal (unique) for block length of 217-1 or up to 16,384bytes.

CRC-32 was a bit more tricky








CRC подсчитывается следующей программой:

;Входные параметры:

;HL - начальный адрес блока

;DE - конечный адрес блока

;Выходные данные:

;BC - контрольная сумма блока

;from Directory System for TR-DOS

;TR-DOS Navigator v0.69b by Юдин С.А.

CRC   PUSH HL

      INC  DE

      LD BC,#0000

CRC1  PUSH DE

      LD A,C

      XOR (HL)

      LD E,A

      PUSH BC

      PUSH HL

      LD BC,#0000

      LD D,#08

CRC2  PUSH BC

      LD A,C

      RRA

      LD A,B

      RRA

      LD B,A

      LD A,C

      RRA

      LD C,A

      POP HL

      LD A,E

      XOR L

      AND 1

      JR Z,CRC3

      LD A,B

      XOR #A0

      LD B,A

      LD A,C

      XOR #01

      LD C,A

CRC3  LD A,E

      RRCA

      AND #7F

      LD E,A

      DEC D

      JR NZ,CRC2

      POP HL

      POP DE

      LD A,D

      XOR C

      LD C,A

      LD A,E

      XOR B

      LD B,A

      INC HL

      POP DE

      LD A,H

      CP D

      JR NZ,CRC1

      LD A,L

      CP E

      JR NZ,CRC1

      DEC DE

      POP HL

      RET










;from deja vue #9

;STORM

;VERY FAST CRC16 WITH TABLE #200.

; ИНСТАЛЛЯЦИЯ ПРОЦЕДУРЫ ВЫЧИСЛЕНИЯ CRC

;        (ПОДГОТОВКА CRCTBL)

CRCINI  LD HL,CRCTBL

        LD C,#10

CRCINI0 LD D,L

        LD E,0

        LD B,8

        EX DE,HL

CRCINI1 ADD HL,HL

        JR NC,CRCINI2

        LD A,C

        XOR H

        LD H,A

        LD A,#21

        XOR L

        LD L,A

CRCINI2 DJNZ CRCINI1

        EX DE,HL

        LD (HL),D

        INC H

        LD (HL),E

        DEC H

        DEC L

        JR NZ,CRCINI0

        RET

;IN: HL-FROM,BC-LEN

;OUT: DE-CRC

CRCCALC LD DE,0

CRCUPD  LD A,C

        LD C,B

        LD B,A

        OR A

        JR Z,$+3

        INC C

        LD A,D

        XOR (HL)

        EXX

        LD H,CRCTBL[+1

        LD B,CRCTBL[

        LD C,A

        LD A,(BC)

        EXX

        XOR E

        EXX

        JR CRCTO

CRCLOOP XOR (HL)

        EXX

        LD C,A

        LD A,(BC)

        XOR (HL)

CRCTO   LD L,C

        EXX

        INC HL

        DJNZ CRCLOOP

        DEC C

        JR NZ,CRCLOOP

        EXX

        LD E,(HL)

        LD D,A

        RET

CRCTBL  EQU $^;[#200]


;STORM

;CRC16 (SLOW)

;IN: HL-FROM;BC-LEN.

;OUT: DE-CRC


CRCCALC LD DE,0

CRCUPD  EX DE,HL

        INC C

        DEC C

        JR Z,$+3

        INC B

        PUSH BC

        POP IX

        LD C,#10

CRC16C  LD A,(DE)

        XOR H

        LD H,A

        LD B,8

CRC16A  ADD HL,HL

        JR NC,CRC16B

        LD A,L

        XOR #21

        LD L,A

        LD A,H

        XOR C

        LD H,A

CRC16B  DJNZ CRC16A

        INC DE

        DEC LX

        JR NZ,CRC16C

        DEC HX

        JR NZ,CRC16C

        EX DE,HL

        RET

Операции с байтами

COMPARE

   .---------------------------------.

   ¦Результат ¦ Состояние ¦ Мнемоника¦

   ¦сравнения ¦  флагов   ¦  условия ¦

   +----------+-----------+----------+

   ¦   A=X    ¦   Z=1     ¦    Z     ¦

   +----------+-----------+----------+

   ¦  A<>X    ¦   Z=0     ¦    NZ    ¦

   +----------+-----------+----------+

   ¦ Беззнаковое сравнение (0...255) ¦

   +----------T-----------T----------+

   ¦   A<X    ¦  CY=1     ¦    C     ¦

   +----------+-----------+----------+

   ¦  A>=X    ¦  CY=0     ¦    NC    ¦

   +----------+-----------+----------+

   ¦ С учетом знака    (-128...+127) ¦

   +----------T-----------T----------+

   ¦   A<X    ¦   S=1     ¦    P     ¦

   +----------+-----------+----------+

   ¦  A>=X    ¦   S=0     ¦    M     ¦

   '----------+-----------+----------'

  Пояснения:

  1) Таблица показывает, какие флаги установятся  при равенстве или отличии

операндов в любую сторону.

  2)  В графе "Мнемоника условия" описываются  проверяемые  флаги  при вводе

команд условного типа.

  3) Обозначения в таблице:

  CY=C=Carry

  NZ=(Z=0)

  NC=(C=0)




Z80 IF-ELSE ; andreadrian.de

To convert a C language if() statement into Z80 assembler is quite nasty in detail:

First, there is only a 8-bit compare op-code in the Z80. To compare larger numbers a subtraction has to be done.

Second, the conditional jump op-code is different for unsigned and signed numbers.

Third, the jump goes to the "else" part of the statement, 

therefore the jump op-code is the logical opposite of the compare operator

Fourth, at the end of the if-part an unconditional jump is needed to the end of the IF-ELSE block.

All these nasty details fit into one table. Never write assembler programs without this table!

.----------------.------------------.-------------------------------.-------------------------snd.

|    C test      |  as subtraction  | unsigned numbers "else jump"  | signed numbers "else jump" |

|----------------X------------------X-------------------------------X----------------------------|

|if (a >= b)     |   if (a-b >= 0)  |  JP C or JR C                 |  JP PO                     |

|if (a < b)      |   if (a-b < 0)   |  JP NC or JR NC               |  JP PE                     |

|if (a == b)     |   if (a-b == 0)  |  cascaded JP NZ or JR NZ      |  cascaded JP NZ or JR NZ   |

'----------------'------------------'-------------------------------'----------------------------'

Note: JP PO is jump on overflow. The overflow flag is the carry 

flag for signed numbers. In the Z80 the overflow and parity 

flag are the same. The official name of JP PO is jump on parity odd. 

As they say "just to confuse the russians".

As example see the assembler listing for the following C code fragment.

  short HL, DE;

  char A;

  HL = -2000;

  DE = -1000;

  if (HL >= DE) {

    A = 1;

  } else {

    A = 0;

  }


Before you run the assembler code below, what is the value 

of A at the end of the C language listing? Even if you are sure,

you may run a C program to confirm your believes. 

In the assembler code we use registers A, DE and HL instead of memory locations.

The C source is given as comment.

;==================================================

; IF-ELSE CODE FRAGMENT

        LD      HL,-2000        ; HL = -2000;

        LD      DE,-1000        ; DE = -1000;

        AND     A               ; if (HL >= DE) {

        SBC     HL,DE

        JP      PO,ELSE

        LD      A,1             ;   A = 1;

        JR      ENDIF

ELSE:                           ; } else {

        LD      A,0             ;   A = 0;

ENDIF:                          ; }

        HALT

        END

Note: The AND A op-code clears the carry flag. 

Because of this, the SBC HL,DE op-code works 

like SUB HL,DE - an op-code the Z80 does not have.


The following assembler listing shows the cascaded if 

(a == b) translation. Because the state of the zero flag does not 

propagate like the carry and overflow flag, after each 

subtraction a conditional jump is necessary. 

The C listing is:

  long HLHL, DEDE;

  char A;

  HLHL = 0x12345678;

  DEDE = 0x12340000;

  if (HLHL == DEDE) {

    A = 1;

  } else {

    A = 0;

  }


The assembler listing is:

;==================================================

; IF-ELSE CODE FRAGMENT 2

        LD      HL,05678H       ; H'L'HL = 0x12345678;

        LD      DE,00000H       ; D'E'DE = 0x12340000;      

        EXX

        LD      HL,01234H      

        LD      DE,01234H      

        EXX

        AND     A               ; if (HLHL == DEDE) {

        SBC     HL,DE

        JR      NZ,ELSE

        EXX

        SBC     HL,DE

        EXX

        JR      NZ,ELSE       

        LD      A,1             ;   A = 1;

        JR      ENDIF

ELSE                            ; } else {

        LD      A,0             ;   A = 0;

ENDIF                           ; }

        HALT

        END

Note: What happens if the first JR NZ,ELSE is deleted?








C (Bit 0)

Carry flag. It is set or cleared depending on the operation is performed. 

For ALU operations, it signs carry (e.g., ADD) or borrow (e.g., SUB). 

For bit shift and rotate operations, it stores the least/most significant 

bit after an operation. For the logical instructions AND, OR, and XOR, 

the Carry flag is reset.


N (Bit 1)

Add/Subtract flag. This flag is used by the Decimal Adjust Accumulator 

instruction (DAA) to distinguish between the ADD and SUB instructions. 

For ADD instructions, N is cleared to 0. For SUB instructions, N is set to 1.


P/V (Bit 2)

Parity/Overflow flag. This flag is set to a specific state depending 

on the operation being performed. For arithmetic operations, this flag 

indicates an overflow condition when the result in the Accumulator 

is greater than the maximum possible number (+127) or is less than 

the minimum possible number (–128). This overflow condition is determined 

by examining the sign bits of the operands.


H (Bit 4)

Half Carry flag. This flag is set or cleared depending on the carry 

and borrow status between bits 3 and 4 of an 8-bit arithmetic operation. 

This flag is used by the Decimal Adjust Accumulator (DAA) instruction 

to correct the result of a packed BCD add or subtract operation.


Z (Bit 6)

Zero flag. It is set if the result generated by the execution of 

certain instructions is 0; otherwise, it is reset.


S (Bit 7)

Sign flag. It stores the state of the most-significant bit of 

the Accumulator (bit 7).







COMPARE CP N

Unsigned

If A == N, then Z flag is set.

If A != N, then Z flag is reset.

If A < N, then C flag is set.

If A >= N, then C flag is reset.


Signed

If A == N, then Z flag is set.

If A != N, then Z flag is reset.

If A < N, then S and P/V are different.

A >= N, then S and P/V are the same.





.--------------------------------------------------.

| if Val2>=Val1 then goto label | >= | JP NC,label |

| if Val2<Val1 then goto label  | <  | JP C,label  |

| if Val2=Val1 then goto label  | =  | JP Z,label  |

| if Val2<>Val1 then goto label | <> | JP NZ,label |

'--------------------------------------------------'




; set Carry flag

 scf

; reset Carry flag (alters Sign and Zero flags as defined)

 or a

; alternate reset Carry flag (alters Sign and Zero flags as defined)

 and a

; set Zero flag (resets Carry flag, alters Sign flag as defined)

 cp a

; reset Zero flag (alters a, reset Carry flag, alters Sign flag as defined)

 or 1

; set Sign flag (negative) (alters a, reset Zero and Carry flags)

 or $80

; reset Sign flag (positive) (set a to zero, set Zero flag, reset Carry flag)

 xor a

;Set parity/overflow (even):

 xor a

;Reset parity/overflow (odd):

 sub a

;Set half carry (hardly ever useful but still...)

 and a

;Reset half carry (hardly ever useful but still...)

 or a

;Set bit 5 of f:

 or %00100000

;get the zero flag in carry 

scf

jr z,$+3

ccf

;Put carry flag into zero flag.

ccf

sbc a, a





;Signed Compare D to E!

;Returns carry and zero flag as expected

;Destroys A.

ld a,d

 sub e

 sbc a,a

 xor e

 xor d

 rla





COMPARE lvd

   ;LD C,4          ;длина

    ;LD B,0          ;ноль

     LD BC,длинна

     LD HL,M1;это сравниваем

     LD DE,M2;с этим

COMPAR1 LD A,(DE)

        INC DE

        CPI 

        JR NZ,NE_SOVPALO ;ret nz

        JP PE,COMPAR1

SOVPALO ;ret




;from Fast Tracker

ld b,xx

compar LD A,(DE)

CP (HL)

INC HL

INC DE

RET NZ

DJNZ compar

RET 





;вместо этого

;  ld bc,42

;  or a

;  sbc hl,bc

;можно так

;  ld  bc,-42

;  add hl,bc


COMPARE 2 bytes

or a

ld hl,(тут ZX или нет?)

ld de,"ZX"

sbc hl,de

ret nz

jr совпало




CPBCDE  PUSH HL

        LD H,B

        LD L,C

        OR A

        SBC HL,DE

        POP HL

        RET 


cphlbc push hl

and a

sbc hl,bc

pop hl

ret





cpstr:

;Compare two strings at HL and DE;returns z if they are equal

;returns c if DE points to the smaller string;returns nc if DE is the 

bigger (or equal) string.

  ld a,(de)

   cp (hl)

   inc de

   inc hl

   ret nz

   or a

   jr nz,cpstr

   ret




COMPARESTRING_nocase:

;Compare two strings at HL and DE, with lowercase being equal to uppercase, 

returns z if they are equal

;returns c if HL points to the smaller string, returns nc if HL is the 

bigger (or equal) string.

;destroys A,C,DE,HL, preserves B

;26 bytes

  ld a,(de)

  call toUpper

  ld c,a

  ld a,(hl)

  call toUpper

  cp c

  inc de

  inc hl

  ret nz

  or a

  jr nz,cpstr

  ret

toUpper:

;A is the char. If A is a lowercase letter, 

this sets it to the matching uppercase

;18cc or 30cc or 41cc avg: 26.75cc

  cp 'a'

  ret c

  cp 'z'+1

  ret nc

  sub 'a'-'A'

  ret




;compare strings

;Compare two strings at HL and DE returns, z if they are equal

;returns c if DE points to the smaller string, 

returns nc if DE is the bigger (or equal) string.

  ld a,(de)

  cp (hl)

  inc de

  inc hl

  ret nz

  or a

  jr nz,cpstr

  ret




COMPARESTRINGS fast nocase:

;Compare two strings at HL and DE, with lowercase being equal to 

uppercase, returns z if they are equal

;returns c if HL points to the smaller string, returns nc 

if HL is the bigger (or equal) string.

;destroys A,C,DE,HL, preserves B

;31 bytes.

  ld a,(de)

  cp 'a'

  jr c,+_

  cp 'z'+1

  jr nc,+_

  sub 'a'-'A'

_:

  ld c,a

  ld a,(hl)

  cp 'a'

  jr c,+_

  cp 'z'+1

  jr nc,+_

  sub 'a'-'A'

_:

  cp c

  inc de

  inc hl

  ret nz

  or a

  jr nz,cpstr

  ret





compare pol

  ld b,lenght

  ld de,m1

  ld hl,m2

COMPARE:        ld      a,(de)          ;porovnava B znaku

                cp      (hl)            ;z adresy DE a HL

                ret     nz              ;Z = stejne

                inc     de

                inc     hl

                djnz    COMPARE




;These code snippets are for 16-bit comparisons.

; NOTE: these are meant to be in-line, not called!

;

;"I learned these from calc84maniac"

;"These have similar flags to that of the `cp` instruction. At the very least,

; you get the zero and carry flag identical."


;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

cpHL_DE:

;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

;Inputs:

;   HL, DE

;Outputs:

;   z flag is set if HL=DE, else nz

;   c flag is set if HL<DE, else nc

;Destroys:

;   none

;size: 4 bytes

;speed: 30cc

;

    or a

    sbc hl,de

    add hl,de



;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

cpHL_BC:

;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

;Inputs:

;   HL, BC

;Outputs:

;   z flag is set if HL=BC, else nz

;   c flag is set if HL<BC, else nc

;Destroys:

;   none

;size: 4 bytes

;speed: 30cc

;

    or a

    sbc hl,bc

    add hl,bc



;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

cpHL_DE_faster:

;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

;Inputs:

;   HL, DE

;Outputs:

;   z flag is set if HL=DE, else nz

;   c flag is set if HL<DE, else nc

;Destroys:

;   A

;size: 6 bytes

;speed: 20cc or 23cc

;

  ld a,h

  cp d

  jr nz,cpHL_DE_result

  ld a,l

  cp e

cpHL_DE_result:


;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

cpHL_BC_faster:

;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

;Inputs:

;   HL, BC

;Outputs:

;   z flag is set if HL=BC, else nz

;   c flag is set if HL<BC, else nc

;Destroys:

;   A

;size: 6 bytes

;speed: 20cc or 23cc

;

  ld a,h

  cp b

  jr nz,cpHL_BC_result

  ld a,l

  cp c

cpHL_BC_result:


;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

cpDE_HL_faster:

;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

;Inputs:

;   DE, HL

;Outputs:

;   z flag is set if DE=HL, else nz

;   c flag is set if DE<HL, else nc

;Destroys:

;   A

;size: 6 bytes

;speed: 20cc or 23cc

;

  ld a,d

  cp h

  jr nz,cpDE_HL_result

  ld a,e

  cp l

cpDE_BC_result:


;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

cpDE_BC_faster:

;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

;Inputs:

;   DE, BC

;Outputs:

;   z flag is set if DE=BC, else nz

;   c flag is set if DE<BC, else nc

;Destroys:

;   A

;size: 6 bytes

;speed: 20cc or 23cc

;

  ld a,d

  cp b

  jr nz,cpDE_BC_result

  ld a,e

  cp c

cpDE_BC_result:


;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

cpBC_HL_faster:

;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

;Inputs:

;   BC, HL

;Outputs:

;   z flag is set if BC=HL, else nz

;   c flag is set if BC<HL, else nc

;Destroys:

;   A

;size: 6 bytes

;speed: 20cc or 23cc

;

  ld a,b

  cp h

  jr nz,cpBC_HL_result

  ld a,c

  cp l

cpBC_HL_result:


;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

cpBC_DE_faster:

;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;:;;

;Inputs:

;   BC, DE

;Outputs:

;   z flag is set if BC=DE, else nz

;   c flag is set if BC<DE, else nc

;Destroys:

;   A

;size: 6 bytes

;speed: 20cc or 23cc

;

  ld a,b

  cp d

  jr nz,cpBC_DE_result

  ld a,c

  cp e

cpBC_DE_result:







if #20 to #5f - then continue, else - out

cp #20

jp c, out

cp #60

jp nc, out




if Bit 7 set - then out, else - continue

bit 7,a

jp nz,out


if bit 7 set - then continue, else - out

bit 7,a

jp z,out

FIND

FIND LENGHT

stringlength:

;Inputs:   HL points to the null-terminated string

;Outputs:   BC is the length of the string   HL points to the byte after the null-byte

;Destroys:   A ;speed: 41+21n ;12 bytes

  xor a

  ld c,a

  ld b,a

  cpir

  scf       ;comment this if the NULL byte should be counted

  sbc a,c

  ld c,a

  sbc a,a

  sub b

  ld b,a

  ret



FIND LENGHT less 256

stringlength_8bit:

; Input: HL points to the null-terminated string (less than 256 bytes long)

; Output: A is the length of the string

  xor  a

  ld   c,a

  cpir

  sub  c

;   dec  a  ; comment if the null byte is supposed to be included in the length

  ret




;IN

        LD HL,0

        LD BC,#FFFF ;сколько байт проверяем

        LD DE,BUFF ;что ищем

        LD IX,#04 ;длина искомого

        CALL FIND

        RET 

;OUT - адрес найденного в BC ... в HL место остановки поиска

;если в BC ноль - ничего не найдено

;очень мне она не нравится!! хотя и ра6отает вроде

FIND    LD (FINL),IX

        LD A,(FINL)

        LD (FADR),DE

FN1     LD DE,(FADR)

        PUSH BC

        LD A,(FINL)

        LD B,A

        PUSH BC

FN3     LD A,(DE)

        CP (HL)

        INC HL

        JR NZ,NOF

        INC DE

        DJNZ FN3

        POP BC

FN0     DEC HL

        DJNZ FN0

        POP BC

        LD B,H

        LD C,L

        RET 

NOF     POP BC

        POP BC

        DEC BC

        LD A,B

        OR C

        JR NZ,FN1

        RET 

FADR    DW #0000

FINL    DW #0000

BUFF    DB "1982"

SWAP

SWAP

;HL - откуда ;DE - куда ;BC - сколько

LD HL,SOURCE1

LD DE,SOURCE2

LD BC,LEN

SWA1 LD A,(DE)

LDI

DEC HL

LD (HL),A

INC HL

JP PE,SWA1 ;JP PE,$-6

RET




;from rom 1982 #343c

ld b,xx

swaper ld a,(de)

ld c,(hl)

ex de,hl

ld (de),a

ld (hl),c

inc hl

inc de

djnz swaper

ex de,hl

ret



swap 2 bottom bits

 ld b,a 

 and 3 

 ld a,b

 jp pe,done 

 xor 3


COPY

ld hl,otkuda ; ld hl, otkuda+dlina-1 (last byte)

ld de,kuda ; ld de, kuda+dlina-1 (last byte)

ld bc,dlina ; ld bc, dlina

ldir ; lddr






;быстрая переброска нескольких байт 

;(снизу вверх!)

ld sp,otkuda

pop hl ;кидаем в стак два байта

pop de ;кидаем в стак два байта

pop bc ;кидаем в стак два байта

pop af ;кидаем в стак два байта

ld sp,kuda

push af ;кладем пару байт куда нам нужно

push bc ;кладем пару байт куда нам нужно

push de ;кладем пару байт куда нам нужно

push hl ;кладем пару байт куда нам нужно






;author: calcmaniac84, I think ;Copy zero terminated string at HL to DE.

strcopy xor a

docopystr cp (hl)

ldi

jr nz,docopystr

ret




Move 3 bytes from (HL) to (DE).

MOVE3 LD B,3

;   Move (B) bytes from (HL) to (DE).

HL2DE LD A,(HL)

LD (DE),A

INC HL

INC DE

DEC B

JP NZ,HL2DE

RET



Block move. (DE) to (HL), (C) bytes total.

DE2HL INC C ;is count down to zero?

DE2HL1 DEC C

RET Z ;yes, we are done.

LD A,(DE) ;no, move one more byte.

LD (HL),A

INC DE

INC HL

JP DE2HL1 ;and repeat.




move block from IX to IY

BLKMOV LD IX, FROM

LD IY, TO

LD BC. COUNT

NEXT LD A, (IX) GET WORD

LD (IY), A

INC IX

INC IY

DEC C

JR NZ, NEXT





Переворот   строки  байтов  (de=начало, bc=длина):

       ld h,b

       ld l,c

       add hl,de ;hl=конец+1

       srl b

       rr c ;bc=wid/2 

mirrorbytes0 

       dec hl

       ld a,(de)

       ldi

       dec hl

       ld (hl),a

       jp pe,mirrorbytes0





;- Reverse a ;input: byte in A ;output: Reversed byte in A

;destroys B ;17 bytes and 66 clock cycles ;author: calcmaniac84

ReverseA:

ld b,a ;b=ABCDEFGH

rrca ;a=HABCDEFG

rrca ;a=GHABCDEF

xor b \ and %10101010 \ xor b ;a=GBADCFEH

ld b,a ;b=GBADCFEH

rrca ;a=HGBADCFE

rrca ;a=EHGBADCF

rrca ;a=FEHGBADC

rrca ;a=CFEHGBAD

xor b \ and %01100110 \ xor b ;a=GFEDCBAH

rrca ;a=HGFEDCBA


;input: B to reverse ;output: A reversed ;25 bytes, 96 clock cycles

typicalreversea:

rr b

rla

rr b

rla

rr b

rla

rr b

rla

rr b

rla

rr b

rla

rr b

rla

rr b

rla

ret





LDIR 4,8,16,32,64....

;This is a general-purpose faster-than-LDIR code to replace LDIR in speed-critical situations.

fastLDIR:

;copy BC bytes from HL to DE

;Cost:;    27cc for having to call;    110cc for setting up the loop, worst case

;    10cc * ceiling(BC/n)        ;n=2^k for some k, see the line below "ldirloop:";    16cc * BC

;costs roughly 152-BC*(5-10/n) more than a simple LDIR (worst case)

;for n=4,  BC>=61 saves;for n=8,  BC>=41 saves;for n=16, BC>=35 saves   * default, see the "ldirloop" to change

;for n=32, BC>=33 saves;for n=64, BC>=32 saves

    push hl

    push af

    xor a

    sub c

    and 15               ;change to n-1

    add a,a

    ld hl,ldirloop

    add a,l

    ld l,a

    jr nc,$+3  ;these aren't needed if the ldirloop doesn't cross a 256 byte boundary. 

;Can save 12cc on the above timings and 3 bytes.

    inc h      ;

    pop af

    ex (sp),hl

    ret

ldirloop:

;n=16, (number of LDI instructions, use qty of 4,8,16,32,64)

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

_ldirloop_end:

    ldi

    jp pe,ldirloop

    ret

 





ldir 4,8,16,32.....

LD HL,SOURCE      ; 10                                

LD DE,DESTINATION ; 10                                

LD BC,bytes       ; 10                                

LOOP LDI               ; 16                                

LDI               ; 16                                

LDI               ; 16                                

LDI               ; 16                                

JP PE,LOOP        ; 10       Loop until bc = zero                    




; Up to 19% faster alternative for large LDIRs (break-even at 21 loops)

; hl = source ; de = destination; bc = byte count

FastLDIR

    xor a

    sub c

    and 16 - 1

    add a,a

    di

    ld (FastLDIR_jumpOffset),a

    ei

    jr nz,$  ; self modifying code

FastLDIR_jumpOffset: equ $ - 1

FastLDIR_Loop:

    ldi  ; 16x LDI

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    ldi

    jp pe,FastLDIR_Loop

    ret




FILL

ld hl,first

ld de,first+1

ld bc,x

ld (hl),a

ldir





ld d,a

ld e,a

ld hl,fill

ld sp,hl

fill push de

jp (hl)





ld (SAVE_SP),sp

ld sp,FINAL_ADDRESS+1

ld hl,fillBYTE_1*#100 + fillBYTE_2

ld de,LENGTH_TO_FILL/2

ld b,e                

dec de         

inc d

PUSHLOOP push hl 

djnz PUSHLOOP

dec d

jr nz,PUSHLOOP

SAVE_SP equ $+1 ld SP,#0000





;заполняем область памяти какой-то текстурой...ну очень тормозно

;FILLHOWLEN EQU #1

;FFILLBEGIN EQU #4000

;FILLEND EQU #57FF

;DE,FILLBEGIN

;IX,FILLEND

;HL,FILLHOW

;C,FILLHOWLEN

;FILLHOW DB "1234567890"

FILL_BYTES

        PUSH HL

        LD B,C  ;   ?

FILL0   LD A,(HL)

        LD (DE),A

        PUSH HL

        PUSH IX

        POP HL

        AND A

        SBC HL,DE

        POP HL

        JR Z,POPR

        JR C,POPR

        INC HL

        INC DE

        DJNZ FILL0

        POP HL

        JR FILL_BYTES

POPR    POP HL

        RET 






 FILLBLOCK with A, bc=len

LD (HL),A

INC HL

DJNZ FILLBLOCK

RET

 





 FILLBLOCK WITH reg D, bc=len

LD A,D

LD (HL),A

  INC HL

DEC BC

LD A,B

OR C

JP NZ,FILLBLOCK2

RET



COUNTERS, LOOPS, etc...

Первый вариант быстрее, и чуть короче, но использует регистр А.

Третий вариант медленнее второго, но зато не привязан к B, можно использовать два любых регистра.

Четвёртый не привязан к А, но имеет ограничение на 32765 значений.

Что лучше - выбирать нужно по конкретной задаче.

такты подсчитаны НЕ КОРРЕКТНО!

  ld bc, 1000

loop dec bc         ; 6t

ld a,b           ; 4t

or c             ; 4t

jr nz, loop    ; 12t

; итого 26 тактов


ld bc, 1000

loop djnz loop    ; 13/8t

dec c           ; 4t

jr nz, loop    ; 12/7t

; итого 29 тактов (чуть меньше, т.к. последний виток быстрее переход)


ld bc, 1000

loop dec c           ; 4t

jr nz, loop    ; 12/7t

dec b           ; 4t

jr nz, loop    ; 12/7t

; итого 32 такта (чуть меньше, т.к. последний виток быстрее переход)


ld de, 1000

loop dec de         ; 6t

bit 7,d         ; 8t

jr z, loop     ; 12t

; итого 26 тактов


ld hl,0

ld bc,1000

loop [код цикла]

cpi

jp pe,loop

На выходе HL=1000






;16bit loop (v1)

  ld  b, e

  dec  de

  inc  d

loop2

...

  djnz loop2

  dec  d

  jp  nz,loop2





;16bit loop (v0)

  ld bc, howmuch

loop:

...

  dec bc

  ld  a, b

  or  c

  jp  nz,loop






    ld b,10         ; The LSB of the loop is 10

    ld d,3          ; The MSB of the loop + the first loop is 3

Loop:

    .....

    djnz Loop

    dec d

    jp nz,Loop

This will loop 522 times




;how much BITS are SET in A ;population counter

;input: A - byte, out: A - number of set bits

;by jacobly

PopCountA

        LD C,A

        AND %10101010

        CPL 

        RRCA 

        ADC A,C

        LD B,A

        AND %00110011

        LD C,A

        XOR B

        RRCA 

        RRCA 

        ADD A,C

        LD C,A

        RRCA 

        RRCA 

        RRCA 

        RRCA 

        ADD A,C

        AND %00001111

        RET 


;а это более длинный и медленный вариант

;input: b ;output: a

TypicalPopCountA:

xor a

ld c, a

rrc b

adc a, c

rrc b

adc a, c

rrc b

adc a, c

rrc b

adc a, c

rrc b

adc a, c

rrc b

adc a, c

rrc b

adc a, c

rrc

adc a, c





JUMP TO PROCEDURES

JPHL

;ld a,num:call jphl (jump to NUMBER proc)

;портит только AF и HL'

        EXX 

        PUSH DE

        LD H,0

        LD L,A

        ADD HL,HL

        LD DE,FUNTBL

        ADD HL,DE

        LD A,(HL)

        INC HL

        LD H,(HL)

        LD L,A

        POP DE

        PUSH HL

        EXX 

        RET 




LD SP,#6000 ;set stack

LD A,2 ;number of procedure

CALL JPHL

RET 

JPHL

;PUSH DE

LD L,A

LD H,0

ADD HL,HL

LD DE,TABLE

ADD HL,DE

LD A,(HL)

INC HL

LD H,(HL)

LD L,A

;POP DE

JP (HL)

TABLE   DW proca,procb,procc,procd,proce.......




;ld a,num: call jphl

jphl  LD HL,JUMP_TBL ;10

   ADD A,A ;4

    ADD A,L ;4

    LD L,A ;4

    ADC A,H ;4

    SUB L ;4

    LD H,A ;4

    LD A,(HL) ;7

    INC HL ;6

    LD H,(HL) ;7

    LD L,A ;4

    JP (HL) ;4

JUMP_TBL DW proca,procb,procc,procd,proce.......









;в AYплейерах часто используется таблица с однобайтным смещением.

;a=x

ld hl,table

ld c,a

ld b,0

add hl,bc

ld c,(hl)

add hl,bc

jp (hl)

table


; пример из ASM 1.12

commands_jphl

ld hl, com_tbl

ld b, 0

ld c, a

add hl, bc

ld c, (hl)

add hl, bc

ld c, b

jp (hl)


com_tbl db com_SHOCK1976-$                  

db com_holdsmpl-$

db com_holdimg-$

db com_holdinstr-$

db com_quant-$

db com_glisup-$

db com_glisdn-$

db com_portdn-$

db com_ZXSOSMBM95-$

db com_portup-$

db com_ZXSOSMBM95-$

db com_sldvol-$

db com_ZXSOSMBM95-$

db iio1-$

db com_ZXSOSMBM95-$











from alonecoder

Ещё  один  способ  вызова функции по номеру (номера заняты не подряд):

;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


   Используется в NedoOS - из-за поддержки нумерации  команд от CP/M и MSX-DOS. 

Самые частые команды лежат в начале списка CPIR.

Когда номера команд идут подряд, CPIR уже не нужен, достаточно перехода по таблице.

Это значительно быстрее!

   Вот вариант от NEO SPECTRUMAN'а:

       ld h,tab ;7

       ld h,(hl) ;7

       jp (hl) ;4

   А вот из Супер Марио под NedoOS:

       ;ld l,a

       or 0xc0 ;7

       ld h,a ;4

       jp (hl) ;4




LD HL,(HL):

Often we want to use indirection when using a lookup table of addresses. 

For example, say you have a look-up table for strings:

LUT

dw String1

dw String2

dw String3

dw String4


String1: db "String1",0

String2: db "String2",0

String3: db "String3",0

String4: db "String4",0


And say you wanted to store the location of the string in HL. 

Assuming HL already points to the address located in the LUT:

 ld e,(hl)

 inc hl

 ld d,(hl)

 ex de,hl


That is 4 bytes, 24 t-states, but it destroys DE. 

The following is the same size and speed, destroying A:

 ld a,(hl)

 inc hl

 ld h,(hl)

 ld l,a


In the case that you need extreme speed or size optimisations, 

the following also does the trick, but has a few drawbacks:

 ld sp,hl

 pop hl

At just 2 bytes, 16-tstates that is pretty optimised, but it destroys

 the stack pointer which is a crucial element to most routines. 

In general, you would need to save the stack pointer somewhere and

 later restore it at a total cost of 40 t-states and 8 bytes and your

 routine wouldn't be able to use the stack. You would then need to use

 this version of indirection at least 6 times to get a speed saving

 and 5 times for a size saving, at the cost of 2 bytes of RAM.

hl,(HL)


;input: HL = pointer to memory, ouput: HL = (HL) ;destroy A only

ldHLind:

 ld a,(hl)

 inc hl

 ld h,(hl)

 ld l,a

 ret

;same as above but destroy DE

ldHLind2:

 ld e,(hl)

 inc hl

 ld d,(hl)

 ex de,hl

 ret


Вывод текста

PRINT 8x8

                ORG     #8000

                LD A,#7F;char

                LD DE,#1F17;XX,YY

;prtchar8x8

;A=char ;de=xy; font in BC

printchar

        LD H,0

        LD L,A

         LD A,E ;convert XY to SCR

         AND 7

         RRCA 

         RRCA 

         RRCA 

         ADD A,D

         LD D,E

         LD E,A

         LD A,D

         AND #18

         OR #40

         LD D,A

        LD BC,#3C00;font

        ADD HL,HL

        ADD HL,HL

        ADD HL,HL

        ADD HL,BC

        LD A,(HL)

        LD (DE),A

        INC HL

        INC D

        LD A,(HL)

        LD (DE),A

        INC HL

        INC D

        LD A,(HL)

        LD (DE),A

        INC HL

        INC D

        LD A,(HL)

        LD (DE),A

        INC HL

        INC D

        LD A,(HL)

        LD (DE),A

        INC HL

        INC D

        LD A,(HL)

        LD (DE),A

        INC HL

        INC D

        LD A,(HL)

        LD (DE),A

        INC HL

        INC D

        LD A,(HL)

        LD (DE),A

        RET 








;from ddp

;in:    [HL] - ASCIIZ-string (incl.ctrl.codes!)

PRINT   LD      A,(HL)

        INC     HL

        OR      A

        RET     Z

        CP      #0D     ;(13) CR+LF

        JR      Z,PRN1

        CP      #16     ;(22) set pos. X, Y

        JR      Z,PRN2

        PUSH    HL

        CALL    PRCHAR

        POP     HL

        JR      PRINT

PRN1    PUSH    HL

        LD      HL,(POSADR)

        LD      A,L

        OR      #1F

        LD      L,A

        CALL    PRCH3   ;Їа®ўҐаЄ  "Є®­Ґж бва®ЄЁ"

        POP     HL

        JR      PRINT

PRN2    LD      E,(HL)

        INC     HL

        LD      D,(HL)

        INC     HL

        LD      A,D

        AND     #07

        RRCA

        RRCA

        RRCA

        OR      E

        LD      E,A

        LD      A,D

        AND     #08

        ADD     #48

        LD      D,A

        LD      (POSADR),DE

        JR      PRINT

;=====

;in:    A - char. (all printable - no ctrl.codes!)

;       [POSADR] - address in screen

PRCHAR  LD      L,A

        LD      H,#00

        ADD     HL,HL

        ADD     HL,HL

        ADD     HL,HL

        LD      DE,#3C00

        ADD     HL,DE

        EX      HL,DE

        LD      HL,(POSADR)

        LD      C,H

        LD      B,8

PRCH1   LD      A,(DE)

        LD      (HL),A

        INC     DE

        INC     H

        DJNZ    PRCH1

        LD      H,C

PRCH3   INC     L

        JR      NZ,PRCH2

        LD      A,H

        ADD     A,#08

        LD      H,A

        CP      #58

        JR      C,PRCH2

        LD      H,#48

PRCH2   LD      (POSADR),HL

        RET

        DEFB    "    A.U.MEMORY TEST (131116)    ",13,0

M_TEST  DEFB    22,1,6,"( pass / fail )"

        DEFB    13,13,"Test: ",0

M_LOOP  DEFB    13,13,"Loop: ",0

M_SLASH DEFB    " / ",0





This code copies the Spectrum ROM font to RAM making it "bold" as it goes,

then sets the systemvariable to point to it:

ld hl,15616 ; ROM font.

ld de,60000 ; address of our font.

ld bc,768 ; 96 chars * 8 rows to alter.

fontb ld a,(hl) ; get bitmap.

rlca ; rotate it left.

or (hl) ; combine 2 images.

ld (de),a ; write to new font.

inc hl ; next byte of old.

inc de ; next byte of new.

dec bc ; decrement counter.

ld a,b ; high byte.

or c ; combine with low byte.

jr nz,fontb ; repeat until bc=zero.

ld hl,60000-256 ; font minus 32*8.

ld (23606),hl ; point to new font.

ret






;from SAVC

pr_str ld de,0

call xy2scr

ld ix,num_str

call pr_str8x8

ret


pr_str8x8 ld a,(ix)

or a

ret z

call pr_smb8x8

call nxt_pr8x8

inc ix

jr pr_str8x8

pr_smb8x8 ld h,0

ld l,a

add hl,hl

add hl,hl

add hl,hl

ld bc,0 ;graph8x8

cp 32

jr c,m2

ld bc,font8x8

m2 add hl,bc

ld b,7

m1 ld a,(hl)

ld (de),a

inc hl

inc d

djnz m1

ld a,(hl)

ld (de),a

ret

nxt_pr8x8 inc de

ld a,d

and %11111000

ld d,a

ret

xy2scr ld a,d

and 7

rrca

rrca

rrca

or e

ld e,a

ld a,d

and 24

or 64

ld d,a

ret

num_str db "123456",0







PRINT VARIABLE STRING OF TEXT!

VARIABLE

;LD (X),BC

  ;LD (XL),DE

;LD (DATA),HL


DI

LD (BLOCK+1),SP

LD A,(Y)

LD L,A

LD H,0

ADD HL,HL

LD DE,BASE

ADD HL,DE

LD SP,HL

LD DE,(DATA)

LD A,(X)

  LD C,A

LD A,(YL)

LD B,A

GOBB1 POP HL

LD A,B

LD (SAME+1),A

LD A,(XL)

LD B,A

LD A,L

OR C

LD L,A

GOBB2 LD A,(DE)

LD (HL),A

INC DE

INC HL

DJNZ GOBB2

SAME LD B,0

DJNZ GOBB1

BLOCK LD SP,0

EI

RET


DATA FOR VARIABLE SPRITE ROUTINE

X DB 0 ;x coordinate variable

Y DB 0 ;y coordinate variable

XL DB 0 ;x length variable

YL DB 0 ;y length variable

DATA DW 0 ;data for variable

PRINT 6x8

print42_str

;BC=coord HL=TEXT,0 ;!!! ПЕРПЕПИСАТЬ!!!!

        LD A,(HL)

        OR A

        RET Z; JR Z,RET_AND_SCROLL

        CP #B0

        JR C,prst1

        CP #E0

        JR C,prst1

        SUB #30

prst1   CALL print42_sym

        INC HL

        LD A, C

        CP #FC

        JR NZ,print42_str

        LD C, 0

        INC B

        JR print42_str

print42_sym

        PUSH AF

        PUSH DE

        PUSH HL

        PUSH BC

        LD H,0

        LD L,A

        ADD HL,HL ;ADD HL,HL;x2

        LD D,H   ;ADD HL,HL;x4

        LD E,L   ;ADD HL,HL;x8

        ADD HL,HL;

        ADD HL,DE;

        LD DE,font42-192;256

        ADD HL,DE

        PUSH BC

        PUSH HL

        LD A,B

        AND #18

        OR #40

        LD D,A

        LD A,B

        AND 7;если печатать в 1 третьей то моwно удалить

        RRCA 

        RRCA 

        RRCA 

        LD E,A

        LD A,C

        RRA 

        RRA 

        RRA 

        AND #1F

        LD H,0

        LD L,A

        ADD HL,DE

     ;LD DE,#3FF;пока не удалять        иначе иногда

        LD A,C

        AND 7

        LD C,A

        JR Z,prs2

prs1    SCF 

        RR D

     ;RR E      ;пока не удалять        срет в символы

        DEC A

        JR NZ,prs1

prs2    LD B,6    ;сколько строк рисуем

prs3    EX (SP),HL

        LD A, (HL)

        INC HL

        EX (SP),HL

        PUSH BC

        LD B,A

        LD A,C

        LD C,0

        OR A

        JR Z,prs5

prs4    SRL B

        RR C

        DEC A

        JR NZ,prs4

prs5    LD A,(HL)

        AND D

        OR B

        LD (HL),A

        INC L

        LD A,(HL)

     ;AND E;пока не удалять

        OR C

        LD (HL),A

        DEC L

        INC H

        POP BC

        DJNZ prs3

        POP HL

        POP BC

prs6    POP BC

        LD A,C

        ADD A,6

        LD C,A

        POP HL

        POP DE

        POP AF

        RET 

Экран и Аттрибуты

SCREEN & ATTRS

#ATTR = FLASH*128+BRIGHT*64+PAPER*8+INK


Процедуры,  связанные  с  адресацией  атрибутов.

Atribute   file:   Length=768   bytes.   Address:   #5800..#5AFF

(22528..23295)

                                                Цвета задаются

Адрес атрибута вычисляется так:               следующими битами:

                                              

 adr(C,L) = 22528+L*32+C                      7  6  543   210

                                              ---------------

где: L - строчка (0..23)                      f  b   p     i

     C - столбец (0..31)                      l  r   a     n

                                              a  i   p     k

Схематически адрес атрибута выглядит так:     s  g   e

                                              h  h   r

        0101 10LL  LLLC CCCC                     t



Пересчет адресов в экранной области

Входные   HL - адрес верхнего  байта нужного знакоместа.

Выходные   HL - соответственный адрес в атрибутах.

        SRL H

        SRL H

        SRL H

        SET 4,H

        SET 6,H


И обратная процедурка...   Входные:   HL - адрес в атрибитах.

Выходные:   HL - адрес верхнего байта соответствующего знакоместа.

        SLA H

        SLA H

        SLA H

        RES 7,H





12)  Как  определить  адрес  атрибута  для  заданного  символа с

координатами XY.

Input:  BC = YX

Output: HL = attribute address

AdrAttr  LD      A,B

         RRCA

         RRCA

         RRCA

         LD      H,A

         AND     #E0

         OR      C

         LD      L,A

         LD      A,H

         AND     3

         OR      #58

         LD      H,A

         RET



13) Переход к следующей строке атрибутов.

Input:  HL = attribute address

Output: HL = upper attribute address

DownAttr LD      A,L        LD      DE,32

         ADD     A,32  или  ADD     HL,DE

         LD      L,A

         RET     NC

         INC     H

         RET


14) Переход к предыдущей строке атрибутов.

Input:  HL = attribute address

Output: HL = lower attribute address


UpAttr   LD      A,L        LD      DE,32

         SUB     32    или  AND     A

         LD      L,A        SBC     HL,DE

         RET     NC

         DEC     H

         RET




 процедурка down_hl_8 для пересчета координат на экране на 8 ниже?

down_hl8

ld a, l

add a, #20

ld l, a

sbc a, a

and #08

add a, h

ld h, a




процедура перехода на 1 тайл (на 16 пикселей) выше:

ld a, l

sub #40

ld l, a

sbc a, a

and #f8 ; A = 0 or -8

add a, h

ld h, a




есть процедура, которая из координат 32х24 высчитывает экранный адрес:

   ;in: D - Y координата, E - X координата

   ;out:DE - screen adress

           LD A,D

           AND 7

           RRCA

           RRCA

           RRCA

           OR E

           LD E,A

           LD A,D

           AND 24

           OR 64

           LD D,A




;Пересчёт из атрибутной области в адресную.

;Там фактически надо переместить биты из младших 0-1 в более старшие 3-4, 

;плюс сохранить адрес. 

;Универсальность процедуры в применимости и по отношению к теневому (#c000-#daff) экрану.

;by GriV

ld a,h ; 4

; and %00000011 ; 7 0  - так и хочется замаскировать эти биты, но эта операция не нужна, 

add a,a ; 4 8  - т.к. старшие биты уйдут по дальнейшей маске %00011111, а 

add a,a ; 4 12  - младшие биты забиты нулями по add a,a

add a,a ; 4 16

xor h ; 4 20

and %00011111 ; 7 27 - сохраним нужные биты и обнулим ненужные 0-2

xor h ; 4 31 - вытащили исходный адрес расположения

ld h,a ; 4 35





;Суть следующая:

;есть DE - адрес в экране

;C - количество пиксельных линий которые надо пропустить вниз

;Надо сделать п/п, которая получит в DE адрес новой пиксельной линии

;by GriV

ld a,d 4 берём уже имеющееся смещение по пикселям вниз

and %00000111 7 маскируем

add a,c 4 добавляем к смещению, т.к. можем перейти через треть или з/м

ld c,a 4 сохранили полученное значение

and %00111000 7 выделили часть, отвечающую за положение внутри трети экрана

rlca 4 смещаем её в старшие биты

rlca 4

add a,e 4 добавляем к младшему адресу

ld e,a 4 сохраняем

sbc a,a 4 если у нас появился CY, значит надо будет делать переход в другую треть

and %01000000 7 заполнили флагом переноса шестой бит аккумулятора

add a,c 4 добавили значения по третям из исходных данных

and %11000000 7 выделили

rrca 4 смещаем в 4-5 бит для адреса экрана

rrca 4 обращаю внимание что полученные трети никуда не сохраняем!

rrca 4

xor c 4 нам надо ещё младшие 3 бита, адрес пиксельной линии

and %11111000 7 по хитрому без промежуточного регистра выделяем её

xor c 4 получили в аккумуляторе

ld b,a 4 спрятали

ld a,d 4 из регистра D забираем треть и сам адрес

and %11111000 4 маскируем их

add a,b 4 добавляем спрятанное значение, младшие 3 бита учтены вначале

ld d,a 4 готово!




Проверка наличие порта #FF

Данная программа  НЕ ОПРЕДЕЛЯТ  неверносделанный порт #FF!

LD HL,#5800  ; заносим в атрибуты

LD DE,#5801  ; число #FF

LD BC,#02FF

LD (HL),#40

LDIR

EI

LD BC,#0800  ; В BC счетчик

HALT

LD HL,FF_YES

LOOP_0  IN A,(#FF)

CP #40

JR Z,LAB_X

DEC BC

LD A,B

OR C

JR NZ,LOOP_0

LD HL,ABSENT

LAB_X   CALL PRINT





; A = Attr byte to set

; B = Line index

ClearLine:

    push af     ; Save A

    ld a,b      ; Multiply the line count with 32

    sla a

    sla a

    sla a

    sla a

    sla a

    ld hl,#5800 ; Calculate the attribute address

    ld d,0

    ld e,a

    add hl,de

    pop af      ; Restore A

    ld b,#20    ; Set all the 32 attribute bytes

SetAttr:

    ld (hl),a

    inc hl

    djnz SetAttr

    ret




find ATTRS from BC

  LD A,B

SRA A

SRA A

SRA A

ADD A,88

LD H,A

LD A,B

AND 7

RRCA

RRCA

RRCA

ADD A,C

LD L,A

RET





clear_attrs

;ei

ld hl,#5800

ld de,#5801

ld bc,#02ff

ld (hl),l

;halt ;дабы плавно))

out (#FE),a

ldir




D_HL    INC     H

LD      A,H

AND     7

RET     NZ

LD      A,L

ADD     A,32

LD      L,A

RET     C

LD      A,H

SUB     8

LD      H,A

RET 





D_DE    INC     D

LD      A,D

AND     7

RET     NZ

LD      A,E

ADD     A,32

LD      E,A

RET     C

LD      A,D

SUB     8

LD      D,A

RET 





U_HL:   LD      A,H

DEC     H

AND     7

RET     NZ

LD      A,L

SUB     32

LD      L,A

RET     C

LD      A,H

ADD     A,8

LD      H,A

RET 





U_DE    LD      A,D

DEC     D

AND     7

RET     NZ

LD      A,E

SUB     32

LD      E,A

RET     C

LD      A,D

ADD     A,8

LD      D,A

RET 




;расчет адреса по знакоместам L-Y  H-X

COOR_Z  LD      A,L

AND     7

RRCA

RRCA

RRCA

ADD     A,H

LD      H,L

LD      L,A

LD      A,H

AND     #18

OR      64

LD      H,A

RET





ATR_CONVERT

LD      A,H

AND     #18

RRCA

RRCA

RRCA

ADD     A,#58

LD      H,A

RET





COOR_ATR

;расчет адреса атрибутов L-Y  H-X

LD      A,H

LD      H,L

LD      L,0

AND     A

DUP     3

RR      H

RR      L

EDUP

ADD     A,L

LD      L,A

LD      A,H

OR      #58

LD      H,A

RET




Переход к предыдущему символу по экранному адресу.

Input:  HL = screen address

Output: HL = previous char screen address

PrevChr  RR      H

RR      H

RR      H

DEC     HL

RL      H

RL      H

RL      H

RET





Как  определить  адрес  атрибута  для  заданного  символа с координатами XY.

Input:  BC = YX

Output: HL = attribute address

AdrAttr  LD      A,B

RRCA

RRCA

RRCA

LD      H,A

AND     #E0

OR      C

LD      L,A

LD      A,H

AND     3

OR      #58

LD      H,A

RET







Переход к следующей строке атрибутов.

Input:  HL = attribute address

Output: HL = upper attribute address

DownAttr LD      A,L LD      DE,32

ADD     A,32  или  ADD     HL,DE

LD      L,A

RET     NC

INC     H

RET





Переход к предыдущей строке атрибутов.

Input:  HL = attribute address

Output: HL = lower attribute address

UpAttr   LD      A,L LD      DE,32

SUB     32    или  AND     A

LD      L,A SBC     HL,DE

RET     NC

DEC     H

RET







На этот раз мы сделали подборку различных подпрограммок по работе с адресами в экранной области.

Конечно же асам программирования все это давно известно, но вот начинающим наверняка будет полезно.

Процедура расчета адреса в файле атрибутов из координат:

На входе: D-X, E-Y

ATR_1   LD A,E

        ADD A,A

        ADD A,A

        ADD A,A

        LD L,A

        LD H,#16

        ADD HL,HL

        ADD HL,HL

        LD A,L

        OR A

        LD L,A

        RET

Второй вариант:

На входе: H-X, L-Y

ATR_2   LD A,H

        ADD HL,HL

        ADD HL,HL

        ADD HL,HL

        LD H,#16

        ADD HL,HL

        ADD HL,HL

        ADD A,L

        LD L,A

        RET

На выходе обеих процедур в HL будет содержаться адрес файла атрибутов.





Решить противоположную задачу можно так:

На входе: HL-адрес

CRDS    LD A,L

        AND #1F

        ADD HL,HL

        ADD HL,HL

        ADD HL,HL

        LD L,H

        LD H,A

        LD A,L

        AND #1F

        LD L,A

        RET

На выходе: H-X, L-Y.




И еще одна простенькая процедура для расчета адреса в экранном файле из адреса в файле атрибутов:

На входе: HL-адрес в файле атрибутов.

ATR_SCR LD A,H

        ADD A,A

        ADD A,A

        ADD A,A

        AND #7F

        LD H,A

        RET

На выходе: HL-адрес экранного файла.






Процедура для расчета адреса в экранном файле из координат.faster

На входе: HL-координаты в знакоместах.

На выходе: HL-адрес в экранном файле.

SCR_ADR LD A,L

        AND 7

        RRCA

        RRCA

        RRCA

        ADD A,H

        LD H,L

        LD L,A

        LD A,H

        AND #18

        OR #40

        LD H,A

        RET




D-X (в знакоместах), E-Y (в знакоместах).slower

На выходе: HL-адрес в экранном файле.

coordde2scrhl   LD      A, E

                AND     #18

                OR      #40

                LD      H, A

                LD      A, E

                AND     7

                AND     A

                RRA 

                RRA 

                RRA 

                RRA 

                ADD     A, D

                LD      L, A

                RET 





Процедура для расчета адреса в экранном файле из координат. Часто применяется при выводе спрайтв.

На входе: D-X (в знакоместах), E-Y (в пикселях).

SCRadr2 LD A,E

        AND 7

        LD C,A

        LD A,E

        AND 192

        RRCA

        RRCA

        RRCA

        OR #40

        OR C

        LD H,A

        LD A,E

        AND 56

        RLCA

        RLCA

        OR D

        LD L,A

        RET

На выходе: HL-адрес в экранном файле.






Процедура для перехода на одну пиксельную линию в экране ниже. На входе: HL-адрес в экранном файле.

LINE_HL INC     H

        LD      A,H

        AND     7

        RET     NZ

        LD      A,L

        ADD     A,32

        LD      L,A

        RET     C

        LD      A,H

        SUB     8

        LD      H,A

        RET

На выходе: HL-адрес в экранном файле ниже на пиксель от исходного.





(c) Д.Анисимов, г.Киров, 1996.

   Хочу предложить в раздел "ЭТЮДЫ" несколько разработанных мною процедур. 

Первая - процедура расчета адреса в файле атрибутов из координат:

Первый вариант:

X-координата в D

Y-координата в E

LD   A,E

ADD  A,A

ADD  A,A

ADD  A,A

LD   L,A

LD   H,#16

ADD  HL,HL

ADD  HL,HL

LD   A,L

OR   A

LD   L,A


Второй вариант:

X-координата в H

Y-координата в L

LD   A,H

ADD  HL,HL

ADD  HL,HL

ADD  HL,HL

LD   H,#16

ADD  HL,HL

ADD  HL,HL

ADD  A,L

LD   L,A

На выходе обеих процедур в HL - адрес файла атрибутов.


   Решить противоположную задачу можно так:

В HL - адрес.

Первый вариант:

LD   A,L

AND  #1F

ADD  HL,HL

ADD  HL,HL

ADD  HL,HL

LD   L,A

LD   A,H

AND  #1F

LD   H,A

на выходе:

в H-y координата

в L-x координата


Второй вариант:

LD   A,L

AND  #1F

ADD  HL,HL

ADD  HL,HL

ADD  HL,HL

LD   L,H

LD   H,A

LD   A,L

AND  #1F

LD   L,A

на выходе:

в H-x координата

в L-y координата.

 


  И еще одна простенькая процедура для расчета адреса в экранном файле из 

адреса в файле атрибутов:

В HL-адрес в файле атрибутов

LD   A,H

ADD  A,A

ADD  A,A

ADD  A,A

AND  #7F

LD   H,A

В HL-адрес экранного файла



from Sergei Smirnov

// Converts X,Y coords to screen address

// Arguments:

// L - coord Y (0..255)

// H - coord X (0..192)

// Returns:

// DE - screen address

coords_to_address:

ld a, h // 4t

and #c0 // 7t

cp #c0 // 7t

jr z, coords_to_address_rom // 7t

// Sum: 25t


srl l // 8t

srl l // 8t

srl l // 8t

// Sum: 24t

// L - coord Y (0..31)

// H - coord X (0..192)

coords_32x192_to_address:

ld a, h // 4t

srl a // 8t

scf // 4t

rra // 4t

srl a // 8t

xor h // 4t

and #f8 // 7t

xor h // 4t

ld d, a // 4t

ld a, h // 4t

rlca // 4t

rlca // 4t

xor l // 4t

and #e0 // 7t

xor l // 4t

ld e, a // 4t

ret // Sum: 78t (102t w/o check)

// Total: 25 + 24 + 78 = 127t, 33 bytes




// Converts X,Y coords to screen address #2

// Arguments:

// L - coord Y (0..255)

// H - coord X (0..192)

// Returns:

// DE - screen address

coords_to_address:

ld a, h // 4t

and #c0 // 7t

cp #c0 // 7t

jr z, coords_to_address_rom // 7t

// Sum: 25t

// block BB

rrca // 4t

scf // 4t

rra // 4t

rrca // 4t

ld d, a // 4t

// Sum: 20t

// block CCC

ld a, h // 4t

and #07 // 7t

or d // 4t

ld d, a // 4t

// Sum: 19t

// block DDD

ld a, h // 4t

and #38 // 7t

rlca // 4t

rlca // 4t

ld e, a // 4t

// Sum: 23t

// block EEEEE

ld a, l // 4t

and %11111000 // 7t

rrca // 4t

rrca // 4t

rrca // 4t

or e // 4t

ld e, a // 4t

ret // Sum: 31t (93t w/o check)

// Total: 25 + 20 + 19 + 23 + 31 = 118t, 31 bytes


coords_to_address_rom:

ld de, 0

scf

ret



// Converts X,Y coords to address in attributes

// Arguments:

// L - coord X (0..255)

// H - coord Y (0..192)

// Returns:

// DE - address in attributes

coords_to_attrs:

ld a, h

rlca

rlca

ld e, a

and #03

cp #03

jr z, coords_to_address_rom


// block BB

or #58

ld d, a


// block DDD

ld a, e

and #e0

ld e, a


// block EEEEE

ld a, l

rrca

rrca

rrca

and #1f

or e

ld e, a

ret




// Converts screen address to address in attributes

// Arguments:

// HL - screen address

// Returns:

// HL - address in attributes

address_to_attrs_hl:

ld a, h

and a

ret z

and #18

rrca 

rrca

rrca

or #58

ld h, a

ret



Кто-то где-то, помнится, просил заливалку экрана сеткой.

Вот, короче не смог придумать. grid

grider ld a,%10101010

ld hl,16384

ld b,24

;grid1 ld c,0

grid2 ld (hl),a

inc l

; dec c

jr nz,grid2

inc h

cpl

djnz grid2

ret





вопрос по теме - ни у кого не сохранилось процедурки down_hl_8 для пересчета координат на экране на 8 ниже?

copy ld a, l

add a, #20

ld l, a

sbc a, a

and #08

add a, h

ld h, a

ret






xy2atr ld a,d

rrca

rrca

rrca

ld d,a

and 224

or e

ld e,a

ld a,d

and 3

or 88

ld d,a

ret



xy2scr ld a,d

and 7

rrca

rrca

rrca

or e

ld e,a

ld a,d

and 24

or 64

ld d,a

ret



scr2atr ld a,d

rrca

rrca

rrca

and %00000011

or %01011000

ld d,a

ret









;Процедура ADR_STR

;Вход:  A - номер строки (0..191)

;Выход: HL,DE - адрес в дисплейном файле.


ADR_STR LD      E,A

        AND     A

        RRA

        SCF

        RRA

        AND     A

        RRA

        XOR     E

        AND     #F8

        XOR     E

        LD      H,A

        XOR     A

        XOR     E

        AND     #C7

        XOR     E

        RLCA

        RLCA

        LD      L,A

        LD      E,A

        LD      D,H

        RET








;from Lop Ears by Players

;FINDS ATTRIBUTE POSITION


ATLOC LD A,B

  SRA A

  SRA A

  SRA A

  ADD A,88

LD H,A

LD A,B

AND 7

  RRCA

RRCA

RRCA

ADD A,C

  LD L,A

RET




from Lop Ears by Players

 ****************************************

 *      SCREEN COORDINATE ROUTINE       *

 ****************************************

  PIXEL LD A,H

  AND 192

  RRCA

RRCA

RRCA

ADD A,64

LD D,A

LD A,H

AND 7

  ADD A,D

  LD D,A

LD A,H

ADD A,A

  ADD A,A

  AND 224

  LD E,A

LD A,L

AND 248

  RRCA

RRCA

RRCA

OR E

LD E,A

EX DE,HL

RET

 





(c) Д.Анисимов, г.Киров, 1996.

   Хочу предложить в раздел "ЭТЮДЫ" несколько разработанных мною процедур. 

Первая - процедура расчета адреса в файле атрибутов из координат:

Первый вариант:

X-координата в D

Y-координата в E

LD   A,E

ADD  A,A

ADD  A,A

ADD  A,A

LD   L,A

LD   H,#16

ADD  HL,HL

ADD  HL,HL

LD   A,L

OR   A

LD   L,A





Второй вариант:

X-координата в H

Y-координата в L

LD   A,H

ADD  HL,HL

ADD  HL,HL

ADD  HL,HL

LD   H,#16

ADD  HL,HL

ADD  HL,HL

ADD  A,L

LD   L,A

На выходе обеих процедур в HL - адрес файла атрибутов.




  Решить противоположную задачу можно так:


В HL - адрес.

Первый вариант:

LD   A,L

AND  #1F

ADD  HL,HL

ADD  HL,HL

ADD  HL,HL

LD   L,A

LD   A,H

AND  #1F

LD   H,A

на выходе:

в H-y координата

в L-x координата





Второй вариант:

LD   A,L

AND  #1F

ADD  HL,HL

ADD  HL,HL

ADD  HL,HL

LD   L,H

LD   H,A

LD   A,L

AND  #1F

LD   L,A

на выходе:

в H-x координата

в L-y координата.






   И еще одна простенькая процедура для расчета адреса в экранном файле из 

адреса в файле атрибутов:

В HL-адрес в файле атрибутов

LD   A,H

ADD  A,A

ADD  A,A

ADD  A,A

AND  #7F

LD   H,A

В HL-адрес экранного файла

888

DEP888

        LD HL,FILE888TO

        CALL LDDE11

        LD HL,FILE888FROM

        LD C,128

DEP     CALL DEP3

        CALL NZ,oldcl

        JR NZ,COLQQ

        EXX 

        PUSH HL

        EXX 

        POP DE

        PUSH BC

        LD BC,#820

        LD A,D

COL80   ADD A,24

        LD D,A

        LDI       ;R

        DEC E

        ADD A,24

        LD D,A

        LDI       ;G

        DEC E

        SUB 48

        LD D,A

        LDI       ;B

        DEC E

        INC A

        DJNZ COL80

        POP BC

        EXX 

        JR COLQ

COLQQ

        EXX 

        LD A,H

COLQ    INC L

        JR Z,$+4

        SUB 8

        LD H,A

        EXX 

        CP FILE888TO/256+24

        JR NZ,DEP

CHLRLC  LD C,(HL)

        INC HL

        RL C

        RET 

COL4

        LD A,#40

        CALL DEPCOL0

        CALL PUTCOL

        DJNZ COL4

        RET 

COL1

        XOR A

        CALL PUTCOL

        DJNZ COL1

        RET 


oldcl

;любое число цветов, кроме 8

;A=1 - старое число цветов и старая палитра

;увеличивает H на 8

        LD D,T888FOUND/256

        DEC A

        JR Z,COLOLD

        LD LX,A

        LD E,A

DEPTAB  CALL DEP3

        DEC E

        LD (DE),A

        JR NZ,DEPTAB

COLOLD  LD A,LX

        LD B,64

        CP 4

        JR NC,COL45O

        DEC A

        JR Z,COL1

        DEC A

        JR Z,COL2

;2=11

;1=10

;0=0

COL3

        LD A,#80

        CALL DEPCOL0

        JR Z,COL3N1

        SLA C

        CALL Z,CHLRLC

        RLA 

        DEC A

COL3N1  CALL PUTCOL

        DJNZ COL3

        RET 

;1=1

;0=0

COL2

        LD A,#80

        CALL DEPCOL0

        CALL PUTCOL

        DJNZ COL2

        RET 

COL45O

        JR Z,COL4

        RRA 

        JR C,COL5

;5=111

;4=110

;3=101

;2=100

;1=01

;0=00

COL6    LD A,#40

        CALL DEPCOL0

        CP 2

        JR C,COL6N1

        DEC A

        SLA C

        CALL Z,CHLRLC

        RLA 

COL6N1  CALL PUTCOL

        DJNZ COL6

        RET 

;0=00

;1=01

;2=10

;3=110

;4=111

COL5    LD A,#40

        CALL DEPCOL0

        CP 3

        JR C,COL5N1

        SLA C

        CALL Z,CHLRLC

        RLA 

        SUB 3

COL5N1  CALL PUTCOL

        DJNZ COL5

        RET 


PUTCOL

        LD E,A

        LD A,(DE)

        EXX 

        RRA 

        RL E ;B

        RRA 

        RL D ;R

        RRA 

        RL C ;G

        JR NC,EXXRET

        LD (HL),E ;B

        LD A,H

        ADD A,24

        LD H,A

        LD (HL),D ;R

        ADD A,24

        LD H,A

        LD (HL),C ;G

        SUB 47 ;INCH

        LD H,A

LDDE11  LD C,1

EXXRET  EXX 

        RET 


DEP3    LD A,#20

DEPCOL0 SLA C

        CALL Z,CHLRLC

        ADC A,A

        JR NC,DEPCOL0

        RET  


Звук

AY8910(12) / YM2149

Частота работы АУка 

amstrad cpc  1,000000 ;странно, но таблицы в тракерах на Амстрад под 2,0мгц

bk0011m      1,500000 (6mhz/2/2 by RDC soft, Moscow ;

zx-pentagon  1,750000 (cpu/2) ;

zx-spectrum  1,773400 ;

zx-melodik   1,789770 (3,57954/2) ;если брать таблицы тональностей этих трех

datasheet    1,7895000 (1,78977) (3,579/2) ;"девайсов", то все эти три таблички будут фактически

msx          1,7897725 (cpu/2) ;одинаковы, ибо частота почти одинаковая

atari st     2,000000 ;






Functional differences AY vs YM

Input clock speed

The YM has an internal clock divider, enabling it 

to be used with an external clock of up to 4Mz vs 2MHz of 

the AY. Pin 26 which is designated TEST 2 on the AY-3-8910 

and should be left NC, is /SEL on the YM2149F. When the 

voltage level of /SEL is high, the input clock is taken as the 

master clock. When the voltage level is low, the input clock 

is divided by 2. The pin has an internal pullup allowing pin 

compatibility with the AY.

Envelope volume

The envelope volume counter on the AY-3-8910 internally uses 4 bit

s, resulting in 16 steps. On the YM2149F it has 5 bits, resulting 

in twice the steps, and counts up twice as fast. This allows for 

smoother volume ramping, even though the register for setting 

its direct value remains 4 bits wide.

Unused bits in registers

On the AY, unused bits in registers always read back as 0 even 

if you had earlier written 1 to them. On the YM2149G, the register

values can be read back as written.

Output voltage offset

The YM2149 has a 2V DC offset on all outputs, whereas 

the AY-3-8910 has a 0.2V offset on a channel if an envelope is active.

Real world sound differences

If you listen to the same compositions played on both chips 

there are differences:

The output from the AY is louder.

Note transitions seem smoother on the AY

The YM output is cleaner







 Музыкальный  процессор включает в себя 16 ре-

гистров.  14 из них используется для формирова-

ния звука.

  Для  того,  чтобы  поместить  в регистр музы-

кального сопроцессора какие либо данные, надо:

1) выбрать регистр для записи данных

   LD BC,#FFFD

   LD A,номер регистра 0 - 13

   OUT (C),A

2) записать данные

   LD BC,#BFFD

   LD A,какoе либо число 0 - 255

   OUT (C),A


  Для чтения данных из регистра надо:

1) выбрать регистр для записи данных

   LD BC,#FFFD

   LD A,номер регистра 0 - 13

   OUT (C),A

2) считать данные

   IN A,(C)

  Первые   шесть   регистров  используются  для

задания   высоты  звучания  каждого  канала  из

диапазона 0 - 4095.

  Регистры R0,R1 - частота звучания канала A.

  Регистры R2,R3 - частота звучания канала B.

  Регистры R4,R5 - частота звучания канала C.


  Регистр R6 - определяет частоту выводимого

  шума для трёх каналов из диапазона 0 - 31.


  Регистр R7 - управляет звуковыми каналами

  d7d6d5d4d3d2d1d0

   x x ¦ ¦ ¦ ¦ ¦ L запрещает звучание канала A.

       ¦ ¦ ¦ ¦ L-- запрещает звучание канала B.

       ¦ ¦ ¦ L---- запрещает звучание канала C.

       ¦ ¦ L------ запрещает шум канала A.

       ¦ L-------- запрещает шум канала B.

       L---------- запрещает шум канала C.


  Следующие   три   регистра  используются  для

задания  громкости  звука  из диапазона 0 - 15.

4-й  бит  указывает  на то, что громкость будет

изменятся   способом   указанным  в  R13  и  со

скоростью указаной в R11/R12.


  Регистр R8  - определяет громкость канала A.

  Регистр R9  - определяет громкость канала B.

  Регистр R10 - определяет громкость канала C.


  Регистры   R11,  R12  -  определяют  скорость

изменения  громкости  звука  от 0 до 65535. (на

практике   изменение   рег.   R11  малоощутимо,

поэтому достаточно задавать только рег. R12)


  Регистр   R13   -   управляет   формированием

огибающей выходного сигнала:


 #0,#1,#2,#3,#9 - затухание, затем тихо;

 #4,#5,#6,#7,#F - нарастание, затем тихо;

 #B - затухание, затем громко;

 #D - нарастание, затем зромко;

 #8 - повторяющееся затухание;

 #C - повторяющееся нарастание;

 #E - повторяющееся нарастание и

      затухание;

 #A - повторяющееся затухание и

      нарастание;


 (C) Денис Паринов, 1997.

  Ну, а теперь  несколько примеров,  демонстрирующих сказаное.

  Перед  обращением  к  программе  в  регистр A

необходимо записать номер эффекта с 1 по 4.

; Вх: A - номер эффекта с 1 - 4.

AY_EFF  LD      HL,EFFECT1 ;Вычисляем

LD      BC,#000E   ;адрес начала

NEFF    ADD     HL,BC      ;эффекта следующего

DEC     A   ;за выбранным.

JP      NZ,NEFF    ;

DEC     HL ;HL на конец выбра-

  ;нного эффекта.

LD      A,#0D      ;Начать с рег-ра R13

LD      C,#FD      ;

NREG    LD      B,#FF      ;Выбрать регистр AY

OUT     (C),A      ;для записи данных.

LD      B,#BF      ;Запись в регистр AY

OUTD       ;байта из (HL) и

  ;уменьшение HL на 1.

DEC     A   ;Уменьшение номера

  ;регистра AY, если

JP      P,NREG     ;он Є 0, продолжить.

RET ;Иначе выйти.


; Эффект используется в различных boot'ах при запуске программ.

EFFECT1 DEFW    #0080,#0001,#0000 ;частота тона

;для каналов A, B, C

DEFB    #00 ;частота шума

DEFB    #38 ;00111000 выкл. шум.

DEFB    #10,#10,#10;громк. канал. A,B,C

DEFW    #1500 ;изменение громкости

DEFB    #01 ;затухание

; Эффект из программы Honey Commander.

EFFECT2 DEFW    #203C,#2064,#208C

DEFB    #00

DEFB    #38

DEFB    #10,#10,#10

DEFW    #0810

DEFB    #01

; Эффект 2 из Honey Commander.

EFFECT3 DEFW    #2050,#2060,#2070

DEFB    #01

DEFB    #00

DEFB    #10,#10,#10

DEFW    #0400

DEFB    #01

; Эффект напоминающий вращение лопастей вертолёта.

EFFECT4 DEFW    #0000,#0000,#0000

DEFB    #00

DEFB    #07

DEFB    #10,#10,#10

DEFW    #0100

DEFB    #0E

 





REGISTERS

                           ---------


        The  AY-3-8910/8912  contains  16  internal registers as follows:

REGISTER          FUNCTION                  RANGE


    0       Channel A fine pitch         8-bit (0-255)

    1       Channel A course pitch       4-bit (0-15)

    2       Channel B fine pitch         8-bit (0-255)

    3       Channel B course pitch       4-bit (0-15)

    4       Channel C fine pitch         8-bit (0-255)

    5       Channel C course pitch       4-bit (0-15)

    6       Noise pitch                  5-bit (0-31)

    7       Mixer                        8-bit (see below)

    8       Channel A volume             4-bit (0-15, see below)

    9       Channel B volume             4-bit (0-15, see below)

   10       Channel C volume             4-bit (0-15, see below)

   11       Envelope fine duration       8-bit (0-255)

   12       Envelope course duration     8-bit (0-255)

   13       Envelope shape               4-bit (0-15)

   14       I/O port A                   8-bit (0-255)

   15       I/O port B                   8-bit (0-255)


        Notes:

        - The AY-3-8912 does not contain register 15.

        -  The  volume  registers  (8, 9 and 10) contain a 4-bit

setting  but if bit 5 is set then that channel uses the envelope

defined by register 13 and ignores its volume setting.

        -  The  mixer  (register  7) is made up of the following

bits (low=enabled):


Bit:  7       6       5       4       3       2       1       0

     _       _

     I/O     I/O   Noise   Noise   Noise    Tone    Tone    Tone

      B       A       C       B       A       C       B       A


        The AY-3-8912 ignores bit 7 of this register.



                           ENVELOPES

                           ---------

        The   AY-3-8910/8912   contains   the  following  preset

envelopes  or  waveforms  (set  using control register 13). Note

that these affect volume only and not the pitch:


         0      __________     single decay then off

         4      /|_________    single attack then off

         8      |||||          repeated decay

         9      __________     single decay then off

        10      /////          repeated decay-attack

                  _________

        11      |              single decay then hold

        12      /|/|/|/|/|/    repeated attack

                 __________

        13      /              single attack then hold

        14      //////         repeated attack-decay

        15      /|_________    single attack then off


example:

        ayctrl  EQU     65533

        aydata  EQU     49149

        start   ld      d,7          ; select the mixer register

                ld      e,62         ; enable channel A only

                call    outer        ; send it to PSG

                ld      d,1          ; channel A course pitch

                ld      e,50         ; pitch value

                call    outer        ; send it to PSG

                ld      d,8          ; channel A volume

                ld      e,15         ; maximum

                call    outer        ; send it to PSG

                ret

        outer   ld      bc,ayctrl    ; select control port

                out     (c),d        ; send specified value

                ld      bc,aydata    ; select data port

                out     (c),e        ; send specified value

                ret





;AY or YM, present or absent

;from Action demo

LD BC,#FFFD

XOR A

OUT (C),A

LD B,#BF

LD A,#40

OUT (C),A

LD B,#FF

IN A,(C)

CP #40

LD HL,ayym_ABSENT ;jr z,present

JR NZ,print ;

LD A,#10

OUT (C),A

IN A,(C)

CP #FF

LD HL,AY8910

JR NZ,print

LD HL,YM2149F

print    CALL PRINT






;alonecoder

Определение наличия музыкального сопроцессора

LD BC,#FFFD

XOR A ;A=0/2/4/7/11 (полные 8-битные)

OUT (C),A ;избегаем OUT (C),0

IN L,(C) ;oldRO

INC L

LD B,#BF

OUT (C),L ;newRO != oldRO

;читать R0 и проверить, изменился ли он 

LD B,#FF ;#fdfd нельзя на ATM1

IN A,(C) ;R0

CP L ;newRO

JR Z,AYON ;R0 == newRO

AY_OFF






AY_OFF

LD HL,#0D00

LD DE,#FFBF

LD C,#FD

RES_AY1 LD B,D

OUT (C),H

LD B,E

OUT (C),L

DEC H

JP P,RES_AY1

RET




ay_off LD HL,#000D

SHUT1 LD BC,#FFFD

OUT (C),L

LD B,#BF

OUT (C),H

DEC L

JR NZ,SHUT1

RET




;DIGITAL GOOD QUALITY BY RAINBOW SOFT

;1BIT SAMPLE АY-3-8910/12 :   

LD      ВС,#FFFD ; инсталляция АY-3-8910/12      

LD      А,7      

0UТ     (С),А    

LD      ВС,#ВFFD

LD      А,#FF    ; вывод звука и шума    

0UТ     (С),А    

LD      ВС,#FFFD

LD      А,8      ; каналЫ : 8 - А, 9 - В, 10 - С

0UТ     (С),А       

LD      НL,начало инструмента    

LD      DE,длина инструмента     

LD      ВС,#ВFFD

L1 PUSН    DE       

LD      А,(НL)   

LD      D,%00001111 ; маска      

EХХ      

LD      В,задержка       

DJNZ    $        

EХХ      

RLА      

LD      E,А      

SВС     А,А      

АND     D        

0UТ     (#FE),А   

0UТ     (#FE),А  

LD      А,E     ¬        

RLА     ¦        

LD      E,А     ¦  повторть 7 раз        

SВС     А,А     ¦        

АND     D       ¦        

0UТ     (#FE),А -        

P0P     DE       

INС     НL       

DEС     DE       

LD      А,D      

0R      E        

JP      NZ,L1    





;BY RAINBOW 8BIT SAMPLE PLAYER

LD      ВС,#FFFD ; инсталляция АУ-3-8910/12

LD      А,7

0UT     (С),А

LD      ВС,#BFFD

LD      А,#FF    ; вывод звука и шума

0UT     (С),А

LD      ВС,#FFFD

LD      А,8      ; каналЫ : 8 - А, 9 - В, 10 - С

0UT     (С),А

LD      HL,начало инструмента

LD      DE,длина инструмента

LD      ВС,#BFFD

L1      LD      А,(HL)

EXX

LD      В,задержка

DJNZ    $

EXX

RRA     ; выделение 4-х старших бит

RRA

RRA

RRA

0UT     (С),А

INC     HL

DEC     DE

LD      А,D

0R      E

JP      NZ,L1





; WARLOCK SYSTEM3 22.12.96

;PLAYER SAMPLES FROM SAMPLER,INSTRUMENT,DIGITAL STUDIO etc 1BIT SAMPLE

ORG 30000

  LD      A,#0D

  LD      HL,OZIW

  LD      DE,20000

  LD      (OZIW).A

  LD      (L1),HL

  LD      (L2),DE

  XOR     A

  LD      C,A

  CALL    LOOP

  INC     C

  XOR     A

  CALL    LOOP

  LD      A,#3E

  LD      C,#07

  CALL    LOOP

  EXX

  LD      BC,#BFFD

  EXX

  LD      BC,#FFFD

  LD      HL,OZIW

L1 EQU     $-2

  LD      DE,#0DE8

L2 EQU     $-2

M1 PUSH    DE

  LD      D,#08

  OUT     (C),D

  LD      A,(HL)

  RRCA

  RRCA

  RRCA

  RRCA

  AND     #0F

  EXX

  OUT     (C),A

  EXX

  INC     D

  OUT     (C),D

  EXX

  OUT     (C),A

  EXX

  INC     D

  OUT     (C),D

  EXX

  OUT     (C),A

  EXX

  LD      A,(OZIW)

L3 DEC     A

  JP      NZ,L3

  PUSH    HL

  POP     HL

  RRCA

  RRCA

  RRCA

  LD      A,(HL)

  RRCA

  RRCA

  RRCA

  RRCA

  AND     #0F

  EXX

  OUT     (C),A

  EXX

  DEC     D

  OUT     (C),D

  EXX

  OUT     (C),A

  EXX

  DEC     D

  OUT     (C),D

  EXX

  OUT     (C),A

  EXX

  LD      A,(OZIW)

L4 DEC     A

  JP      NZ,L4

  INC     HL

  POP     DE

  DEC     DE

  LD      A,D

  OR      E

  JR      NZ,M1

  XOR     A

  LD      C,#08

  CALL    LOOP

  INC     C

  XOR     A

LOOP      PUSH    BC

  PUSH    DE

  LD      D,C

  LD      BC,#FFFD

  OUT     (C),D

  LD      B,#8F

  OUT     (C),A

  POP     DE

  POP     BC

  RET

OZIN ; начиная  с  этого адреса должен располагаться инструмент






;DIGIPLAYER BY A.VOROZHKIN?

   DI

   LD HL,Адрес

   LD DE,Длина

   LD BC,#FFFD

   LD A,Канал     ;8 - A,9 - B,10 - C

   OUT (C),A

   LD B,#BF       ;_OUT1

   LD A,(HL)

   OUT (C),A

   LD B,Задержка  ;от 1 до 255

   DJNZ $

   INC HL

   DEC DE

   LD A,D

   OR E

   JR NZ,_OUT1

   EI

   RET






(c) MOn5+Er^Sa9e

Супер-быстрый player with MCC

   Без вступлений и окончаний приведу самый распоследний вариант

плейера  для  MCC  метода!  Теперь им можно воспроизводить ВСЕ -

теорема Котельникова играет на нас!

EI      ;до запуска в порт AY n7 записать #FF!

HALT

LD SP,START-SAMPLE-ADRESS

LD IX,LOOP

LD DE,#08+256*('MCC_TBL+1)

LD BC,#FFFD

OUT (C),E       ;вкл. 8 AY_порт (крайний канал)

LOOP:   POP HL

LD E,L

LD A,(DE)

DEC D

EX AF,AF'

LD A,(DE)

LD E,9

OUT (253),A

EX AF,AF'

OUT (C),E

OUT (253),A

LD E,H

LD A,(DE)

INC D

EX AF,AF'

LD A,(DE)

LD E,8

OUT (253),A

EX AF,AF'

OUT (C),E

OUT (253),A

JP (IX)

Обработчик INT'а:

INT:    POP IY  ;взяли адр. возврата

PUSH HL ;восстановили испорченный SAMPL

LD HL,0

ADD HL,SP

LD BC,END_SAMPLE_ADDRESS

OR A

SBC HL,BC

JR NC,END_INT

LD BC,#FFFD

POP HL

JP (IY)

END_INT: выход из player'а

160 тактов на две выборки сэмпла.

80 тактов на выборку.

Fдискрет.=3,5 МГц/80=43,75 кГц.

Плеер  использует  только один крайний канал AY, можно и два для

двойной громкости (на качество в принципе почти не влияет кол-во

крайних каналов...)

Единственный  недостаток,  можно слегка проскочить конец сэмпла,

т.к.  проверка  раз  в  INT, но недостаток легко исправляется: в

конце  сэмпла (а он обязательно беззнаковый 0..#FF, а не -128 до

+127) заполнен 896 байт "тишиной" - байтом #80 и всё. Почему 896

- т.к. в INT'е 71680 тактов (maximum на нетурбированной машине),

а 71680/80=896!




;ay play sound

w8912  ld hl,snddat        ; start of AY-3-8912 register data.

       ld e,0              ; start with register 0.

       ld d,14             ; 14 to write.

       ld c,253            ; low byte of port to write.

w8912a ld b,255            ; 255*256+253 = port 65533 = select soundchip register.

       out (c),e           ; tell chip which register we're writing.

       ld a,(hl)           ; value to write.

       ld b,191            ; 191*256+253 = port 49149 = write value to register.

       out (c),a           ; this is what we're putting there.

       inc e               ; next sound chip register.

       inc hl              ; next byte to write.

       dec d               ; decrement loop counter.

       jp nz,w8912a        ; repeat until done.

       ret

snddat defw 0              ; tone registers, channel A.

       defw 0              ; channel B tone registers.

       defw 0              ; as above, channel C.

sndwnp defb 0              ; white noise period.

sndmix defb 60             ; tone/noise mixer control.

sndv1  defb 0              ; channel A amplitude/envelope generator.

sndv2  defb 0              ; channel B amplitude/envelope.

sndv3  defb 0              ; channel C amplitude/envelope.

sndenv defw 600            ; duration of each note.

       defb 0




TRACKER'S TONE TABLES




;----------------------------------------------------------------------------------------------


Итак таблица которую лично я задектил впервые у Дэвида Виттакера 

(он писал в ней с 1986 года, например в игре Zub 128k)

Её же испозовали поляки в SOUND TRACKER, и наши ребята в Pro Tracker 1 и 2

(так же используется в Fast Tracker 1.хх, Global Tracker, Vortex Tracker, ST Pro и где-то еще)

Назовем ее Классической, потому что львиная часть музыки для спектрума 

написана именно в данной таблице тональностей.

(в Pt2 данная табличка с небольшим косяком, но сути не меняет вцелом)

Таблица легко делается сдвигами (делением)

Ориентирована на частоту 2,0 мгц (почти))

  C     #C    D    #D     E     F    #F     G    #G     A    #A     B

ClassicToneTable DW #EF8, #E10, #D60, #C80, #BD8, #B28, #A88, #9F0, #960, #8E0, #858, #7E0

DW #77C, #708, #6B0, #640, #5EC, #594, #544, #4F8, #4B0, #470, #42C, #3F0

DW #3BE, #384, #358, #320, #2F6, #2CA, #2A2, #27C, #258, #238, #216, #1F8

NoteC_4 DW #1DF, #1C2, #1AC, #190, #17B, #165, #151, #13E, #12C, #11C, #10B, #0FC

DW #0EF, #0E1, #0D6, #0C8, #0BD, #0B2, #0A8, #09F, #096, #08E, #085, #07E

DW #077, #070, #06B, #064, #05E, #059, #054, #04F, #04B, #047, #042, #03F

DW #03B, #038, #035, #032, #02F, #02C, #02A, #027, #025, #023, #021, #01F

DW #01D, #01C, #01A, #019, #017, #016, #015, #013, #012, #011, #010, #00F

длина 192 байт (12 нот на 8 октав)


;----------------------------------------------------------------------------------------------


Тут уже моя таблица которая используется в Fast Tracker версии выше 1.00 

Делал я ее с учетом таблицы Классической от Виттакера, но с учетом частоты Пентагон 128к

т.е. под 1,75 mhz

(как оказалось, по звучанию тона похожа на нативную табличку 

из Vortex Tracker II Pro Tracker 3.4-3.5 

(одна из популярных?)

Sand's variant of table (for pentagon aka 1.75mHz+)

Ориентирована на 1,75 мгц 

FTR1xxToneTable DW #D10, #C58, #BA0, #B00, #A60, #9C8, #940, #8B8, #840, #7C0, #750, #6F0

DW #688, #62C, #5D0, #580, #530, #4E4, #4A0, #45C, #420, #3E0, #3A8, #378

DW #344, #316, #2E8, #2C0, #298, #272, #250, #22E, #210, #1F0, #1D4, #1BC

NoteC_4 DW #1A2, #18B, #174, #160, #14C, #139, #128, #117, #108, #0F8, #0EA, #0DE

DW #0D1, #0C5, #0BA, #0B0, #0A6, #09C, #094, #08B, #084, #07C, #075, #06F

DW #068, #062, #05D, #058, #053, #04E, #04A, #045, #042, #03E, #03A, #037

DW #034, #031, #02E, #02C, #029, #027, #025, #022, #021, #01F, #01D, #01B

DW #01A, #018, #017, #016, #014, #013, #012, #011, #010, #00F, #00E, #00D

длина 192 байт (12 нот на 8 октав)

А вот и таблица из Вортаха... (теоретически писалась под 1,75 мгц, отчасти

схожа с моей табличкой из Fast Tracker 1.xx)

Проверять лениво, но видимо эта табла из Pt3 под названием ASM or PSC (почему-то)))

Ориентирована на 1,75 мгц

DW #d10, #c55, #ba4, #afc, #a5f, #9ca, #93d, #8b8, #83b, #7c5, #755, #6ec

DW #688, #62a, #5d2, #57e, #52f, #4e5, #49e, #45c, #41d, #3e2, #3ab, #376

DW #344, #315, #2e9, #2bf, #298, #272, #24f, #22e, #20f, #1f1, #1d5, #1bb

NoteC_4 DW #1a2, #18b, #174, #160, #14c, #139, #128, #117, #107, #0f9, #0eb, #0dd

DW #0d1, #0c5, #0ba, #0b0, #0a6, #09d, #094, #08c, #084, #07c, #075, #06f

DW #069, #063, #05d, #058, #053, #04e, #04a, #046, #042, #03e, #03b, #037

DW #034, #031, #02f, #02c, #029, #027, #025, #023, #021, #01f, #01d, #01c

DW #01a, #019, #017, #016, #015, #014, #012, #011, #010, #00f, #00e, #00d

длина 192 байт (12 нот на 8 октав)


;----------------------------------------------------------------------------------------------


Таблица из S.Q.Tracker (плеер Витамина), авторы тракера скорее всего выбрали

ее именно из-за того что эта таблица максимально близка (практически точно) к 

частоте Мелодика (чехословацкий доп.девайс с АУ8910)

Такую же таблицу использовал Линдон Шарп в Капитан Динамо и в Баббл Диззи

(в данных играх Линдон Шарп (ну или автор музыкальной инжины) скорее всего руководствовался частотой 

из даташита на Микросхему AY891X 

Частота MELODIK и частота из DATASHEET на АУк очень схожи = 1,789770 мелодик 

и 1,789500 - в даташите, к слову на MSX АУк также работает на схожей частоте - 1,7897725 мгц)

ориентирована на частоту 1,789770 мгц

SQTrackerToneTabble DW #d5d, #c9c, #be7, #b3c, #a9b, #a02, #973, #8eb, #86b, #7f2, #780, #714

DW #6ae, #64e, #5f4, #59e, #54f, #501, #4b9, #475, #435, #3f9, #3c0, #38a

DW #357, #327, #2fa, #2cf, #2a7, #281, #25d, #23b, #21b, #1fc, #1e0, #1c5

NoteC_4 DW #1ac, #194, #17d, #168, #153, #140, #12e, #11d, #10d, #0fe, #0f0, #0e2

DW #0d6, #0ca, #0be, #0b4, #0aa, #0a0, #097, #08f, #087, #07f, #078, #071

DW #06b, #065, #05f, #05a, #055, #050, #04c, #047, #043, #040, #03c, #039

DW #035, #032, #030, #02d, #02a, #028, #026, #024, #022, #020, #01e, #01c

DW #01b, #019, #018, #016, #015, #014, #013, #012, #011, #010, #00f, #00e

длина 192 байт (12 нот на 8 октав)


;----------------------------------------------------------------------------------------------


А вот и та самая табличка из ПЗУ 128к, так же встречал ее в каком-то трэкере на Амстраде

Её же использовали парни из Players в своих трэках (а может и тот самый тракер на 

Амстраде?), В Астро Марине Корпс похожая таблица. 

Применялась так же (возможно в сдвинутом виде) в самой первой демоверсии Фаста

прям академическая можно сказать табличка )))

Ориентирована вот именно в таком виде на 1,773400 мгц ...довольно точно

ROM128ToneTable DW #FBF, #EDC, #E07 

DW #D3D, #C7F, #BCC, #B22, #A82, #9EB, #95D, #8D6, #857, #7DF, #76E, #703

DW #69F, #640, #5E6, #591, #541, #4F6, #4AE, #46B, #42C, #3F0, #3B7, #382

DW #34F, #320, #2F3, #2C8, #2A1, #27B, #257, #236, #216, #1F8, #1DC, #1C1

NoteC_4 DW #1A8, #190, #179, #164, #150, #13D, #12C, #11B, #10B, #0FC, #0EE, #0E0

DW #0D4, #0C8, #0BD, #0B2, #0A8, #09F, #096, #08D, #085, #07E, #077, #070

DW #06A, #064, #05E, #059, #054, #04F, #04B, #047, #043, #03F, #03B, #038

DW #035, #032, #02F, #02D, #02A, #028, #025, #023, #021, #01F, #01E, #01C

DW #01A, #019, #018, #016, #015, #014, #013, #012, #011, #010, #00F, #00E

DW #00D, #00C, #00C, #00B, #00B, #00A, #009, #009, #008

длина 216 байт (12 нот на 8 октав + допольнительные)


Эта таблица использовалась в ASC Sound Macter (ASM) от Андрея Сендицкого и

в редакторе Pro Sound Creator от KVA

(отталкивались они от таблицы из ПЗУ 128к, ну и сдвинули на пару тонов,

видимо для близкого созвучия с Классической таблицей от Виттакера)

полагаю похожа на Классическую по звучанию, или на таблицу из ПЗУ 128К Бэйсика (если ее сдвинуть)

(в pt3.4r есть похожая таблица)

Ориентирована на 2,0 Мгц в таком варианте (но если выбрать иную С-4.... то, ну вы понимаете)

так же по звучанию близка к таблице от Фоллина, особенно с 4ой октавы


ASC_PSC_ToneTable DW  #edc, #e07 ;subcontr

DW  #d3e, #c80, #bcc, #b22, #a82, #9ec, #95c, #8d6, #858, #7e0, #76e, #704

DW  #69f, #640, #5e6, #591, #541, #4f6, #4ae, #46b, #42c, #3f0, #3b7, #382

DW  #34f, #320, #2f3, #2c8, #2a1, #27b, #257, #236, #216, #1f8, #1dc, #1c1

NoteC_4 DW  #1a8, #190, #179, #164, #150, #13d, #12c, #11b, #10b, #0fc, #0ee, #0e0

DW  #0d4, #0c8, #0bd, #0b2, #0a8, #09f, #096, #08d, #085, #07e, #077, #070

DW  #06a, #064, #05e, #059, #054, #050, #04b, #047, #043, #03f, #03c, #038

DW  #035, #032, #02f, #02d, #02a, #028, #026, #024, #022, #020, #01e, #01c

;DW  #01a, #019, #017, #016, #015, #014, #013, #012, #011, #010 not used in ASM

длина 176 байт (в ASM - 12 нот на 7 октав, плюс еще две ноты в субконтр октавы)


Табличка из Pro Sound Maker от Дратова Дениса. Все та же таблицу из ПЗУ 128к

но со сдвигом на несколько тонов (зачем? слух? частота?)

а вот и ответ:

Дратов просто убрал "лишние" частоты (субконтр октава, например) для более корректного

звучания на частотах 1,7ххх

Ориентирована в таком виде на 1,7734 мгц (естественно что звучать она будет как табла из ПЗУ 128к)

PsmToneTable DW  #D3D, #C7F, #BCB, #B22, #A82, #9EB, #95D, #8D6, #857, #7DF, #76E, #703

DW  #69F, #63F, #5E6, #591, #541, #4F6, #4AE, #46B, #42C, #3F0, #3B7, #382

DW  #34F, #320, #2F3, #2C8, #2A1, #27B, #257, #236, #216, #1F8, #1DC, #1C1

NoteC_4 DW  #1A8, #190, #179, #164, #150, #13D, #12C, #11B, #10B, #0FC, #0EE, #0E0

DW  #0D4, #0C8, #0BD, #0B2, #0A8, #09F, #096, #08D, #085, #07E, #077, #070

DW  #06A, #064, #05E, #059, #054, #04F, #04B, #047, #043, #03F, #03B, #038

DW  #035, #032, #02F, #02D, #02A, #028, #025, #023, #021, #01F, #01E, #01C

DW  #01A, #019, #018, #016, #015, #014, #013, #012, #011, #010, #00F, #00E

длина 192 байт (12 нот на 8 октав)


;----------------------------------------------------------------------------------------------


А вот и таблица которую использовал Тим Фоллин (и, далеко не только он)

Такая же таблица найдена в тракере SoundTrakker-128, StArkos на Амстрад и

Equinoxe Tracker от UBI Soft на том же Амстраде, так же данная таблица в слегка урезаном виде

есть в редакторе Magic Sound от Henri Bittner 1987 года)

Использовалась в игре Lode Runner 128k

рассчитана на 2,0 мгц

(возможно что Фоллин использовал эту таблицу на спектруме со сдвигом)

(так же как поступил Дратов в ПСМ с табличкой из РОМ128....взять за С_4 тон #1AA)

(а может быть и нет - может Фоллин писал прямо на Амстраде, история пока умалчивает)

Данная таблица частично похожа на таблицу Виттакера, но У виттакера скажем так - упрощенный вариант.



;;; dw #EEE, #E18, #D4D

AmstradFolToneTable dw #EEE, #E18, #D4D, #C8E, #BDA, #B2F, #A8F, #9F7, #968, #8E1, #861, #7E9

dw #777, #70C, #6A7, #647, #5ED, #598, #547, #4FC, #4B4, #470, #431, #3F4

dw #3BC, #386, #353, #324, #2F6, #2CC, #2A4, #27E, #25A, #238, #218, #1FA

NoteC_4_2_0 dw #1DE, #1C3, #1AA, #192, #17B, #166, #152, #13F, #12D, #11C, #10C, #0FD

dw #0EF, #0E1, #0D5, #0C9, #0BE, #0B3, #0A9, #09F, #096, #08E, #086, #07F

dw #077, #071, #06A, #064, #05F, #059, #054, #050, #04B, #047, #043, #03F

dw #03C, #038, #035, #032, #02F, #02D, #02A, #028, #026, #024, #022, #020

dw #01E, #01C, #01B, #019, #018, #016, #015, #014, #013, #012, #011, #010

;;; dw #010, #010, #010, #010

длина 206  байт (фактический рабочий 192 байта)

;----------------------------------------------------------------------------------------------


Так называемая таблица натуральных величин (или типа того) автор Siril

ориентирована на 1,520640

NaturlScldToneTable DW #B40, #A8C, #A00, #960, #900, #870, #7E9, #780, #708, #6C0, #654, #600

DW #5A0, #546, #500, #4B0, #480, #438, #3F5, #3C0, #384, #360, #32A, #300

DW #2D0, #2A3, #280, #258, #240, #21C, #1FA, #1E0, #1C2, #1B0, #195, #180

NoteC_4 DW #168, #152, #140, #12C, #120, #10E, #0FD, #0F0, #0E1, #0D8, #0CB, #0C0

DW #0B4, #0A9, #0A0, #096, #090, #087, #07F, #078, #071, #06C, #065, #060

DW #05A, #054, #050, #04B, #048, #044, #03F, #03C, #038, #036, #033, #030

DW #02D, #02A, #028, #026, #024, #022, #020, #01E, #01C, #01B, #019, #018

DW #017, #015, #014, #013, #012, #011, #010, #00F, #00E, #00E, #00D, #00C

длина 192 байт (12 нот на 8 октав)







;---------------------------------------------------------------------------------------------

ProTracker3.3-3.4r TABLE_PROTRACKER3_3 (эта таблица отличается от нижней - стороной округления)

в Оригинальном Pt3.1 от GDC используется именно эта таблица

В самом тракере называется PROTRACKER

DW #c21, #b73, #ace, #a33, #9a0, #916, #893, #818, #7a4, #736, #6ce, #66d

DW #610, #5b9, #567, #519, #4d0, #48b, #449, #40c, #3d2, #39b, #367, #336

DW #308, #2dc, #2b3, #28c, #268, #245, #224, #206, #1e9, #1cd, #1b3, #19b

DW #184, #16e, #159, #146, #134, #122, #112, #103, #0f4, #0e6, #0d9, #0cd

DW #0c2, #0b7, #0ac, #0a3, #09a, #091, #089, #081, #07a, #073, #06c, #066

DW #061, #05b, #056, #051, #04d, #048, #044, #040, #03d, #039, #036, #033

DW #030, #02d, #02b, #028, #026, #024, #022, #020, #01e, #01c, #01b, #019

DW #018, #016, #015, #014, #013, #012, #011, #010, #00f, #00e, #00d, #00c

длина 192 байт (12 нот на 8 октав)


в середине рассчитана на 1,625 мгц

ProTracker3.4-3.5 TABLE_PROTRACKER3_4 (эта таблица отличается от верхней - стороной округления)

вроде как дело рук ММцМ (используется в новых версиях ПТ3, насколько я понимаю)

DW #c22, #b73, #acf, #a33, #9a1, #917, #894, #819, #7a4, #737, #6cf, #66d

DW #611, #5ba, #567, #51a, #4d0, #48b, #44a, #40c, #3d2, #39b, #367, #337

DW #308, #2dd, #2b4, #28d, #268, #246, #225, #206, #1e9, #1ce, #1b4, #19b

DW #184, #16e, #15a, #146, #134, #123, #112, #103, #0f5, #0e7, #0da, #0ce

DW #0c2, #0b7, #0ad, #0a3, #09a, #091, #089, #082, #07a, #073, #06d, #067

DW #061, #05c, #056, #052, #04d, #049, #045, #041, #03d, #03a, #036, #033

DW #031, #02e, #02b, #029, #027, #024, #022, #020, #01f, #01d, #01b, #01a

DW #018, #017, #016, #014, #013, #012, #011, #010, #00f, #00e, #00d, #00c

длина 192 байт (12 нот на 8 октав)



Еще одна таблица под названием Real была использована в Pro Tracker 3.x (плеер Витамина)

Видимо делалась одним из авторов доработок PT3 (тоже самое что и выше но сдвинута слегка....)

в середине рассчитана на 1,625 мгц, сдвинута на тон (вроде как ее зачем юзал WYZ tracker в первых версиях)

RealPT3ToneTable DW  #cda, #c22, #b73, #acf, #a33, #9a1, #917, #894, #819, #7a4, #737, #6cf

DW  #66d, #611, #5ba, #567, #51a, #4d0, #48b, #44a, #40c, #3d2, #39b, #367

DW  #337, #308, #2dd, #2b4, #28d, #268, #246, #225, #206, #1e9, #1ce, #1b4

DW  #19b, #184, #16e, #15a, #146, #134, #123, #112, #103, #0f5, #0e7, #0da

DW  #0ce, #0c2, #0b7, #0ad, #0a3, #09a, #091, #089, #082, #07a, #073, #06d

DW  #067, #061, #05c, #056, #052, #04d, #049, #045, #041, #03d, #03a, #036

DW  #033, #031, #02e, #02b, #029, #027, #024, #022, #020, #01f, #01d, #01b

DW  #01a, #018, #017, #016, #014, #013, #012, #011, #010, #00f, #00e, #00d

длина 192 байт (12 нот на 8 октав)

;----------------------------------------------------------------------------------------------




Таблица с MSX использовалась в PSG Tracker (c)1992 Flying Bytes

очень близка к таблице из ПЗУ 128к Бэйсика на спектруме

dw  #D5D, #C9C, #BE7, #B3C, #A9B, #A03, #973, #8EB, #86B, #7F2, #780, #714

dw  #6AE, #64E, #5F4, #59E, #54D, #501, #4B9, #475, #435, #3F9, #3C0, #38A

dw  #357, #327, #2FA, #2CF, #2A7, #281, #25D, #23B, #21B, #1FC, #1E0, #1C5

dw  #1AC, #194, #17D, #168, #153, #140, #12E, #11D, #10D, #0FE, #0F0, #0E2

dw  #0D6, #0CA, #0BE, #0B4, #0AA, #0A0, #097, #08F, #087, #07F, #078, #071

dw  #06B, #065, #05F, #05A, #055, #050, #04C, #047, #043, #040, #03C, #039

dw  #035, #032, #030, #02D, #02A, #028, #026, #024, #022, #020, #01E, #01C

; dw     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0

;----------------------------------------------------------------------------------------------


TURBO SOUND FM

 Программирование Turbo Sound by Shiru Otaku

                Выбор чипа


   Оба  чипа  управляются одними и теми же стандартными портами (#FFFD - выбор регис─

тра AY, #BFFD - вывод  значения  в регистр AY). В один  момент  времени к этим портам 

подключается один из чипов, и все выводы в регистры AY направляются в него.При сбросе

выбирается чип номер 0. Переключение между чипами  производится путём выбора регистра

AY с номером,находящимся вне диапазона существующих  регистров. Выбор  регистра #FF

подключает нулевой чип, #FE - первый.

   Условная процедура выбора 0-го чипа:

 selChip0: 

         LD BC,#FFFD

         LD A,#FF

         OUT (C),A

        RET


   Аналогично происходит выбор 1-го чипа:

 selChip1: 

         LD BC,#FFFD

         LD A,#FE

         OUT (C),A

        RET


   Можно сделать простую процедуру,выбирающую нужный чип  по его номеру в аккумуляторе (0...1):

 selChip: 

         LD BC,#FFFD

        AND 1  ; во избежание ошибок

               ; при числе в аккумуляторе,

                ; не равном 0 или 1

         XOR B

         OUT (C),A

        RET


   Полная  совместимость TS  со всем ранее написанным  ПО обеспечивается при условии,

что  оно  не  пытается  производить  выбор несуществующих  регистров  AY  (на текущий

момент программ, не отвечающих этому условию, не замечено).


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


   Так как второй чип не имеет собственных управляющих портов, обеспечивается возмож─

ность  использовать любые ранее написанные процедуры  вывода звука без всяких дорабо─

ток. Например,есть две процедуры: проигрывающая  музыку  и  проигрывающая  звуковые

эффекты. На  одном  чипе  для проигрывания звуковых  эффектов  параллельно  с музыкой

приходится временно выключать один или несколько каналов музыки, чтобы дать возмож─

ность  проиграться  звуковому эффекту. Приналичии Turbo Sound можно запустить музыку

на одном чипе, а эффекты на втором,используя те же процедуры. Нужно только вставить

переключение чипов между ними.

   Например, было:

          CALL musicPlay

        CALL soundPlay


   Стало:

         CALL selChip1

         CALL musicPlay

         CALL selChip0

        CALL soundPlay


   При  отсутствии  TS новый вариант будет работать точно так же, как старый.

   На текущий момент не существует специализированных  плееров  музыки под TS. Но в

тестовых  целях возможно осуществить такое проигрывание, создав два обычных трёхкана─

льных модуля (в каждом из них должно содержаться  по три канала шестиканальной ком─

позиции) и откомпилировав их  в разные адреса. Для  проигрывания  достаточно  будет

сделать следующее:

         CALL selChip1

         CALL player1

         CALL selChip0

        CALL player0


   При  этом  каждому  из плееров не нужно знать, на какой чип он выводит данные.

   Важный  момент: после завершения работы с TS нужно выбирать нулевой чип. Также это

нужно делать перед выполнением программного сброса.В противном случае возможны про─

блемы  с работой устройств, подключаемых к портам  ввода-вывода AY, так как на разъём

TS заводятся только линии портов ввода-вывода нулевого чипа. Примеры кода,приведён─

ные выше, учитывают это требование.


      Другие реализации Turbo Sound


   Всё вышесказанное относилось к работе с TS версии  NedoPC. Но существует также два 

старых  варианта схемы Turbo Sound: QUADRA от  Amazing Soft Making  и  вариант  TS от

Power of Sound. 

   Вариант  QUADRA полностью несовместим с NedoPC  TS - в  нём предполагается наличие 

собственных управляющих портов для второго чипа (#EEFD - выбор  регистра  AY, #AFFD -

вывод  значения). Поддержка этого варианта схемы лишена смысла ввиду его непопулярно─

сти и полном отсутствии программного обеспечения.

   Вариант TS  от Power of Sound поддержан в единственном  на данный момент редакторе

шестиканальной музыки, Turbo Sound Editor. С точки зрения программирования он отлича─

ется только способом переключения чипов, что позволяет легко добавить его поддержку

в создаваемом  для NedoPC TS  программном обеспечении.Для этого достаточно предоста─

вить  пользователю возможность выбора типа TS  и соответствующим  образом  доработать 

процедуры выбора чипа.

   Переключение  чипов в PoS TS происходит посредством  вывода  значения  0..1 в порт

#1F (номер  выбираемого чипа соответствует выводимому значению).

   Ниже  приводится вариант универсальной процедуры  выбора чипа по его номеру в ак─

кумуляторе. Предполагается,что тип TS сохранён по адресу chipType+1, нулевое значе─

ние  соответствует  варианту TS от NedoPC, ненулевое - варианту от PoS.


 selChip: 

        AND 1

 chipType: 

        LD B,0  ; вместо нуля присутствует

              ;число, соответствующее типу

 TS 

         DEC B

         JR NC,chipPoS

         LD C,#FD

         XOR B

         OUT (C),A

        RET

 chipPoS: 

         OUT (#1F),A

        RET


    Автоопределение наличия и типа TS


   Ниже приводится текст процедуры автоматического  определения  наличия  и типа TS

(варианты NedoPC и PoS ), использующаяся в Turbo Sound Editor . Следует учесть, что в 

случае невозможности чтения значений регистров  AY  (встречается  в редких случаях)

автоматическое определение работать не будет.Поэтому желательно предусматривать во─

зможность указания типа TS 'вручную'.


;Turbo-Sound checker by Himik's ZxZ/PoS-WT 

;24.05.05 at work ;) 

 ;Found: 

   ;No AY/YM chip on board

   ;Single AY/YM chip on board

   ;Turbo-Sound port by PoS & Bitwalker

                      ;(port #1F for swith)

   ;Turbo-Sound port by NedoPC

             ;(registers #FE-#FF selection)

        ORG #61A8

 C_1 

         DI

         XOR A

         LD HL,#FE00

         LD DE,#FFBF

         LD BC,#FFFD

         OUT (C),B   ;SELECT TS AY0 CHRV

         OUT (C),A   ;SELECT REG 0

         LD B,E

         OUT (C),B   ;#BF-> REG 0 AY0 CHRV

         INC A

         OUT (#1F),A ;SELECT TS AY1 POS

         OUT (C),C   ;#FD-> REG 0 AY1 POS

         LD B,D

         OUT (C),H   ;SELECT TS AY1 CHRV

         OUT (C),L   ;SELECT REG 0

         LD B,E

         OUT (C),H   ;#FE-> REG 0 AY1 CHRV

         LD A,L

         OUT (#1F),A ;SELECT TS AY0 POS

         OUT (C),L   ;#00-> REG 0 AY0 POS

         INC A

         OUT (#1F),A ;SELECT TS AY1 POS

         LD B,D

         OUT (C),D   ;SELECT TS AY0 CHRV

         OUT (C),L   ;SELECT REG 0

         IN A,(C)    ;READ BYTE FROM REG 0

        CP C

 ;переходим, если найден TS by NedoPC 

        JR Z,TS_ENABLE_CHRV

 ;переходим, если найден TS by PoS 

         CP #FE

        JR Z,TS_ENABLE_POS

 ;переходим, если ни одного чипа не найдено 

         CP #FF

        JR Z,NO_AY

C_2 

;найден только один чип 

 TE_DISABLE 

         LD A,1

         OUT (#FE),A

        RET

 NO_AY 

         LD A,2

         OUT (#FE),A

        RET

 TS_ENABLE_CHRV 

         LD A,4

         OUT (#FE),A

        RET

 TS_ENABLE_POS 

         LD A,6

         OUT (#FE),A

        RET

DISPLAY /A, "Length: ",C_2-C_1 

Shiru (NedoPC team) 







проверка наличия TSFM

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 present 

tfmpresent 

       ld (TURBOFMON),a

BEEPER

;из нового Fast Tracker'a

Beep LD BC, #0418 ;04 = громкость

LD A, (BorderColor)

XOR C

OUT (#FE), A

bee1 DJNZ bee1

XOR C

OUT (#FE), A

RET





        xor a

loopy   xor %00010000   ;bits ---S-BBB        S=Sound B=Border

        out (#fe),a

        ld bc,3000      ;Lower number=higher pitch

pausey  dec c

        jr nz,pausey

        dec b

        jr nz,pausey       

        jr loopy





;1BIT SAMPLE PLAYER FOR BEEPER BY RAINBOW SOFT

  LD      HL,начало инструмента

LD      DE,длина инструмента

L1 LD      А,(HL)

LD      С,%00010000 ; маска

LD      В,задержка

DJNZ    $

RLA

LD      В,А

SBC     А,А

AND     С

0UT     (#FE),А

LD      А,В     ¬

RLA     ¦

LD      В,А     ¦  повторть 7 раз

SBC     А,А     ¦

AND     С       ¦

0UT     (#FE),А -

INC     HL

DEC     DE

LD      А,D

0R      E

JP      NZ,L1





;Steve Turner / Hewson Cons 

;sfx engineused in Ranarama, Quazatron, IronMan 

;можно вешать на прерывания или вызывать в основном цикле

;you can use this routine on interrupts (as autor) or in main game cicle

sound ld a,(sonreq) ;новый звук играем?

and a

jr z,nonew  ;нет

;да

ld (sonnow),a

dec a

jr z,noise ;#01 шум

ld hl,sfx_data

dec a

add a,a

add a,a

add a,a

ld e,a

xor a

ld (sonreq),a

ld d,a

add hl,de

ld bc,08

ld de,sonfrq

ldir

jr process

nonew ld a,(sonnow) ;а старый звук есть?

and a

ret z

dec a ;продолжать шум?

jr nz,process ;продолжать звук

jr cnois

noise ld a,0ah

ld (sonlen),a

xor a

ld (sonreq),a

cnois ld b,30h

gain call random

and 10h

out (0feh),a

ld c,02h

make dec c

jr nz,make

djnz gain

ld hl,sonlen

dec (hl)

ret nz

xor a

ld (sonnow),a

ret

process ld a,(sonfrq)

ld h,a

ld a,10h

ld d,0ffh

sonlp ld e,h

out (0feh),a

xor 10h

freq dec d

jr z,mod

dec e

jr nz,freq

jr sonlp

mod ld a,(soncfg)

add a,h

ld (sonfrq),a

ld hl,sonmod

dec (hl)

ret nz

ld hl,sonlen

dec (hl)

jr nz,modify

xor a

ld (sonnow),a

ld a,(sonnex)

and a

ret z

ld (sonreq),a

ret

modify ld a,(sobrsf)

ld c,a

ld a,(sontyp)

and a

jr z,reset

dec a

jr z,typ1

dec a

jr z,typ2

typoth ld a,(soncfg)

neg

ld (soncfg),a

jr mode

typ2 inc c

inc c

ld a,c

ld (sobrsf),a

jr reset

typ1 dec c

dec c

ld a,c

ld (sobrsf),a

jr reset

reset ld a,c

ld (sonfrq),a

mode ld a,(sonrnd)

ld (sonmod),a

ret

random push hl

ld hl,(rnseed)

inc hl

ld a,h

and 03

ld h,a

rok ld (rnseed),a

ld a,r

xor (hl)

pop hl

ret

rnseed defw 1000h

sonfrq defb 00 ;начальная частота ;start frequency

soncfg defb 00 ;скорость изменения частоты ;frequency change

sonmod defb 00 ;количество модуляций в звуке ;change times

sonlen defb 00 ;количество повторений звука ;repeat times

sontyp defb 00 ;вид модуляции ;modulate type

;0 sawtooth

;1 2nd mod down

;2 2nd mod up

;3+ triangle

sobrsf defb 00 ;частота сброса ;reset frequency

sonrnd defb 00 ;темп изменения частоты сброса ;change reset temp

sonnex defb 00 ;приклеенный эффект ;linked sfx

sonnow defb 00 ;что играем ;

sonreq defb 00 ;меняем эффект ;

sfx_data

;here all souned excepts number 1 reserved for random noise

; defb   0,   5,   5,   1,   0,   0,   0,   0

; defb 28h,   5, 0Ah,   1,   0,   0,   0,   0

; defb   0, 80h, 1Eh,   1,   0,   0,   0,   0

; defb      0,   2, 1Eh,   1,   0,   0,   0,   0

; defb      0, 7Dh, 20h,   1,   0,   0,   0,   0

; defb 0FFh, 83h, 20h,   1,   0,   0,   0,   0

; defb 0FFh, 83h, 28h, 20h,   1, 3Ch,   1,   0

; defb 0F0h,0F0h,   8,   3,   0, 3Ch,   6,   0

; defb      2, 80h, 0Ah,   1,   0,   0,   0,   0

; defb  28h,0FAh,   8,   1,   0,   0,   0,   0

; defb 0FAh, 2Ch,   6, 0Ah,   1, 5Ah,   1,   0

; defb      0,0FCh, 14h,   8,   1, 50h,   8,   0

; defb 0E6h,0E6h,   4,   1,   1,   0,   0,   0

; defb  2Dh, 43h, 14h,   1,   1,   0,   0,   0

; defb   0,   0,   0,   0,   0,   0,   0,   0

; defb   0,   0,   0,   0,   0,   0,   0,   0

; defb   0,   0,   0,   0,   0,   0,   0,   0

; defb   0,   0,   0,   0,   0,   0,   0,   0

; defb   0,   0,   0,   0,   0,   0,   0,   0

; defb   0,   0,   0,   0,   0,   0,   0,   0

COVOX

Теперь конкретно о воспроизведении

Digital эффектов:

Оцифрованный звук представляет из себя

конечный набор восьмиразрядных байт, характе-

ризующих значения амплитуды цифруемых колеба-

ний в дискретные моменты времени...

Для воспроизведения оцифровок нужно подавать

в порты управления громкостью эти байты с

определенной частотой...

Распространены два вида форматов Digital звука

1.Wav-файлы: Оцифровка производиться по 256-

  бальной шкале (0..255), только положительной 

  части звуковой волны...

2.Aif-файлы: Оцифровка производиться по двум

  128-бальным шкалам:

  Положительная часть волны (0...128)

  Отрицательная часть волны (-128...0)


Рассмотрим как же воспроизвести оцифр. звук:

Если у вас есть COVOX, то значения байтов из

WAV-файла целиком и полностью подаются в порт

данных COVOX`а. Для проигрывания AIF-файлов к

каждому байту перед подачей его на COVOX надо

прибавить число 128...

Это обуславливается тем, что COVOX -это уст-

ройство для воспроизведения восьмибитных WAV-

-файлов... и AIF необходимо преобразовать для

корректного проигрывания COVOX`ом...

А теперь программа:


         ORG 30000

COVOX   EQU #FB

START   EQU 35000

LENGTH  EQU 5000

PAUSA   EQU 20         

         XOR A

         OUT (COVOX),A

         LD HL,START

         LD DE,LENGTH

LOOP     LD A,(HL)

***      ADD A,128

         OUT (COVOX),A

         LD B,PAUSA

         DJNZ $

         INC HL

         DEC DE

         LD A,D

         OR E

         JR NZ,LOOP

         RET


Если же у вас нет COVOX`а,но есть AY,то можно

воспроизвести DIGITAL звук на нем, хотя каче-

ство будет похуже:         

             

         ORG 30000

START   EQU 35000

LENGTH  EQU 5000

PAUSA   EQU 20

CHANEL  EQU 10   

         LD BC,#FFFD

         LD A,#07

         OUT (C),A

         LD B,#BF

         LD A,#FF

         OUT (C),A

         LD HL,START

         LD DE,LENGTH

         LD BC,#FF

         LD A,CHANEL

         OUT (C),A

LOOP     LD B,#BF

         LD A,(HL)

***      ADD A,128

         SRL A

         SRL A

         SRL A

         OUT (C),A

         LD B,PAUSA

         DJNZ $

         INC HL

         DEC DE

         LD A,D

         OR E

         JR NZ,LOOP

         RET

Переменные используемые в программах имеют

следующие значения:

START-адресс хранения в памяти оцифровки.

LENGTH-длина оцифровки.

COVOX-порта COVOX`а (обычно #FB для PENT128,

и 231 для SCORPIONA256)...

CHANEL-канал для воспроизведения(см. выше)

PAUSA-самый главный параметр.Он определяет с

какой частотой будет проигрываться оцифровка.

Вообще говоря PAUSA задает время паузы между

двумя соседними выводами в звук. канал...

В обоих случаях звездочкой помечены строчки,

которые необходимо оставить, если воспроизво-

дятся AIF-файлы, и надо удалить для воспроиз-

ведения WAV-файлов...   

Вот в принципе и все что надо знать чтобы вос-

произвести оцифровку...

Hо данный способ воспроизведения является

частотовремязависимым, т.е. при изменении час-

тоты изменяется соответствующим образом и вре-

мя воспроизведения инструмента...








DI                     

LD   HL,ADR  ;начало   

LD   DE,LEN  ;длина    

   CYC LD   A,(HL)            

OUT  (251),A ;COVOX    

LD   B,N     ;N-замед- 

   TORM DJZN TORM    ;ление    

INC  HL                

DEC  DE                

LD   A,D               

OR   E                 

JR   NZ,CYC            

EI                     

RET 


Управление

KEYBOARD

;keyboard bits-ports table by sand/mhm

     bit:  0   1   2   3   4 ,  4   3   2   1   0  :bit

----port .---+---+---+---+---|---+---+---+---+---. port----

 3 | #F7 | 1 ¦ 2 ¦ 3 ¦ 4 ¦ 5 | 6 ¦ 7 ¦ 8 ¦ 9 ¦ 0 | #EF | 4

         .---+---+---+---+---|---+---+---+---+---.

 2 | #FB | Q ¦ W ¦ E ¦ R ¦ T | Y ¦ U ¦ I ¦ O ¦ P | #DF | 5

         :---+---+---+---+---|---+---+---+---+---:

 1 | #FD | A ¦ S ¦ D ¦ F ¦ G | H ¦ J ¦ K ¦ L ¦ENT| #BF | 6

         '---+---+---+---+---|---+---+---+---+---'

 0 | #FE |CS ¦ Z ¦ X ¦ C ¦ V | B ¦ N ¦ M ¦SS ¦SPC| #7F | 7

         '---+---+---+---+---'---+---+---+---+---'





WAITFORKEY:

xor a

in a, (#fe)

cpl

and #1f ;%00011111

jp z, WAITFORKEY

....key pressed




anykey

XOR A

IN  A, (#FE)

CPL

AND #1F

RET


;ожидание отпускания клавиши

anykeypushed

call anykey

jr nz,anykeypushed

ret



;опрос BREAK и EDIT (например в качестве выхода)

key_Break

LD A, #FE ;BREAK (CS+SPACE) and EDIT MODE (CS+1)

IN  A, (#FE)

RRA ;1

RET C

LD A, #7F

IN  A, (#FE)

RRA ;SPC

RET NC

LD A, #F7

IN  A, (#FE)

RRA ;CS

RET







;ПРОЦЕДУРА KEY ВОЗВРАЩАЕТ ФЛАГ Z=0,ЕСЛИ НА КЛАВИАТУРЕ ЧТО-НИБУДЬ НАЖАТО.

KEY     XOR     A

        IN      A,(#FE)

        AND     %00011111

        CP      %00011111

        RET







;Y or N wait  (какой-то из них вроде глючит в FUSE)

        LD BC,#DFFE                 ;---------OLD KEYS----------

        IN A,(C)                    ; XOR A

        LD A,#20                    ; IN A,(#FE)

        IN A,(#FE)                  ; CPL

        AND B                       ; AND #1F

        OR #E0                      ; JR Z,KEYS

        INC A                       ; LD A,#DF

        JR Z,KEYS                   ; IN A,(#FE)

        BIT 4,B                     ; AND #10

        JR Z,POKE   ;Y              ; JR Z,POKE ;Y

        JR NZ,GAME  ;N              ; JR NZ,GAME ;N

                                    ;---------------------------






; Опросник нажатой клавиши через #5c08 из Фаста, например

KeyScaner XOR A

LD HL,  SYS5C00+8 ;последняя нажатая

LD (HL), A

KS1 EI

HALT

OR  (HL)

JR Z, KS1

CP  #20

JR NZ, ksz

LD A, #FE

IN  A, (#FE)

RRA

LD A, #20

JR C, ksz

LD A, #1F

ksz LD (HL), 0

RET







Колесников Сергей

KEY  HALT      ;Ждем следующее прерывание

     XOR A     ;обнуляем аккумулятор.


     LD A,#F7  ;заносим  в аккумулятор

               ;наш полуряд (третий).

     IN A,(#FE);младший бит адреса порта.

     BIT 0,A   ;определяем состоян. бита, ;RRCA

               ;соответствующ.клавише "1"

               ;если бит активен, то флаг

               ;Z=1, если нет, то Z=0.

     JR Z,BLUE ;если Z=1, то операция     ;JR C,BLUE

               ;JR Z... будет выполняться

               ;если Z=0, мы ее "проско-

               ;чили".


     LD A,#F7  ;опять наш полуряд.

     IN A,(#FE);опрос клавы.

     BIT 1,A   ;определяем состояние пер-

               ;вого бита, который соот-

               ;ветствует клавише "2".

     JR Z,RED  ;здесь аналогично предыду-

               ;щему.

     JR KEY    ;больше опрашивать ничего

               ;не надо, "зацикливаемся".


BLUE LD A,1    ;в аккумулят. - цвет синий

     OUT (254),A; BORDER 1.

     RET

RED  LD A,2    ;двойка соответст. красному

     OUT (254),A; BORBER 2.

     RET



Следует  заметить,  что  есть  еще один

практически такой же способ опроса клавиа-

туры, который отличается от вышеизложенно-

го тем, что спецификация проверяемого ряда

находится в регистровой паре BC, а код на-

жатой  клавиши  передается в регистр А. То

есть:


LD BC,nnnn

IN A,(C)   ;ввод данных из порта

           ;в аккумулятор.


   Здесь  nnnn - адрес внешнего порта, ус-

танавливаемого  в BC. Надо только помнить,

что в регистре С  будет #FE (постоянно), а

в В - значение, соответствующее номеру по-

луряда. Например, для  нашей  первой прог-

раммки вместо:

     LD A,#F7

     IN A,(#FE)


следует набрать:

     LD BC,#F7FE

     IN A,(C)









; Title: ZX Spectrum Keyboard Routines

; Author: Dean Belfield

; Created: 29/07/2011

; Last Updated: 29/07/2011

; Requires:

; Modinfo:

;; Read the in-game controls

; HL: The control map

; Returns:

;  A: Input flags - 000UDLRF (Up, Down, Left, Right, Fire)

; Zero flag set if no key pressed

Read_Controls: LD D, 5 ; Number of controls to check

LD E, 0 ; The output flags

LD C,0xFE ; Low is always 0xFE for reading keyboard

Read_Controls1: LD B,(HL) ; Get the keyboard port address

INC HL

IN A,(C) ; Read the rows in

AND (HL) ; And with the mask

JR NZ, Read_Controls2 ; Skip if not pressed (bit is 0)

SCF ; Set C flag

Read_Controls2: RL E ; Rotate the carry flag into E

INC HL

DEC D

JR NZ, Read_Controls1 ; Loop

LD A,E ; Fetch the key flags

AND A ; Check for 0

RET

; As Read_Keyboard, but with debounce

Read_Keyboard_Debounce: CALL Read_Keyboard ; A debounced versiion - Read the keyboard

AND A ; Quick way to do CP 0

JR NZ, Read_Keyboard_Debounce ; Loop until key released

1xx CALL Read_Keyboard ; And second loop reading the keyboard

AND A ; CP 0

JR Z, 1xx ; Loop until key is pressed

RET 

; Read the keyboard and return an ASCII character code

; Returns:

;  A: The character code, or 0 if no key pressed

; BC: The keyboard port (0x7FFE to 0xFEFE)

;

Read_Keyboard: LD HL,Keyboard_Map ; Point HL at the keyboard list

LD D,8 ; This is the number of ports (rows) to check

LD C,#FE ; Low is always 0xFE for reading keyboard ports

Read_Keyboard_0: LD B,(HL) ; Get the keyboard port address

INC HL ; Increment to keyboard list of table

IN A,(C) ; Read the row of keys in

AND #1F ; We are only interested in the first five bits

LD E,5 ; This is the number of keys in the row

Read_Keyboard_1: SRL A ; Shift A right; bit 0 sets carry bit

JR NC,Read_Keyboard_2 ; If the bit is 0, we've found our key

INC HL ; Go to next table address

DEC E ; Decrement key loop counter

JR NZ,Read_Keyboard_1 ; Loop around until this row finished

DEC D ; Decrement row loop counter

JR NZ,Read_Keyboard_0 ; Loop around until we are done

AND A ; Clear A (no key found)

RET

Read_Keyboard_2:       LD A,(HL) ; We've found a key at this point; fetch the character code!

RET

Keyboard_Map: DB #FE,"#","Z","X","C","V"

DB #FD,"A","S","D","F","G"

DB #FB,"Q","W","E","R","T"

DB #F7,"1","2","3","4","5"

DB #EF,"0","9","8","7","6"

DB #DF,"P","O","I","U","Y"

DB #BF,"#","L","K","J","H"

DB #7F," ","#","M","N","B"

Input_Custom: DB #FB, %00000001 ; Q (Up)

DB #FD, %00000001 ; A (Down)

DB #DF, %00000010 ; O (Left)

DB #DF, %00000001 ; P (Right)

DB #7F, %00000001 ; Space (Fire)









; FROM JERRI

;scan_kbd -вешается на прерывания сканирование клавиатуры

; -остальные подпрограммы работают с массивом созданым этой подпрограммой

;HL,BC,AF


;scan_ctrl -проверяет нажатые клавиши согласно таблице k_table до 8 штук

;HL,DE,BC,AF,HL',DE',BC'

;A         -результат


;test_joypad - проверка управления scan_ctrl 

;HL,DE,BC,AF,HL',DE',BC'

;   - в ячейке key_presed только что нажатых кнопок

;   - в ячейке key_holded удерживаемых кнопок


;scan_tbl -используется при задании таблицы сканкодов для k_table

;HL,BC,AF

;BC       -результат B-ряд 0-7 С-маска #10-#01


;key_char     -сканирование нажатой клавиши 

;HL,DE,BC,AF

;A,key_presed -результат


;test_key -проверка нажатия любой новой клавиши через key_char

;HL,DE,BC,AF

;A        -клавиша

;Carry    -нажата новая клавиша 

;NotCarry -удерживаемых старая клавиша или ничего не нажато 


;test_kemp  -тестирование подключенного kempston joystikа с 1ой кнопкой 

;HL,DE,BC,AF

;A,kemp_act -#00 джойстик присутсвует

;   -#c9 джойстик не найден


;по схеме

; store_kemp_act

; call test_kemp

; xor #c9

; xor store_kemp_act

; ld (kemp_act),a

;можно включать/выключать обработку джойстика


;------------------------------------

scan_kbd

ld a,-2

ld c,a

ld hl,kb_buf

dup 7

ld b,a

ini

rlca

edup

ld b,a

ini

ret

;------------------------------------

scan_ctrl

ld bc,k_table

ld de,kb_buf

exx

ld e,#80

scan_ctrl0

exx

ld a,(bc)

inc bc

ld l,a

ld h,0

add hl,de

ld a,(bc)

inc bc

and (hl)

cp 1

exx

rr e

jr nc,scan_ctrl0

ld a,e

kemp_act ret

in a,(#1f)

and #1f

or e

ret

;------------------------------------

test_joypad

call scan_ctrl

ld c,a

ld a,(key_holded)

xor c

and c

ld (key_presed),a

ld a,c

ld (key_holded),a

ret

;------------------------------------

scan_tbl

ld hl,kb_buf

ld b,8

scan_tbl0

ld c,#10

scan_tbl1

ld a,(hl)

and c

cp 1

jr c,scan_tbl2

rrc c

jr nc,scan_tbl1

inc hl

djnz scan_tbl0

ld c,b

ret

scan_tbl2

ld a,8

sub b

ld b,a

ret

;------------------------------------

key_char

ld de,keys_table

ld hl,kb_buf

ld c,8

key_char0

ld a,(hl)

inc hl

ld b,5

key_char1

rra

jr nc,key_char2

inc de

djnz key_char1

dec c

jr nz,key_char0

key_char2

ld a,(de)

ld (key_presed),a

ret

;------------------------------------

test_key

call key_char

ld c,0

old_key equ $-1

ld (old_key),a

cp c

scf

ccf

ret z

cp 1

ccf

ret

;------------------------------------

test_kemp

ld bc,#001f

ld l,b

ld e,b

i_00

in a,(c)

or e

ld e,a

dec l

jr nz,i_00


ld a,e

and c

jr z,kemp_present

ld a,#c9

kemp_present

ld (kemp_act),a

ret

;------------------------------------

k_table

; p o a q Sp En H R

; 0 1 2 3 4 

; r l d u f 


db 5, 1 ;right p   5, 1

db 5, 2 ;left  o   5, 2

db 1, 1 ;down  a   1, 1

db 2, 1 ;up    q   3, 1


db 7, 1 ;fire  spc 7, 1

db 6, 1 ;key6  en  6, 1


db 2, 8 ;key7  R   2, 8

db 5, 16 ;key8  Y   5, 16


key_presed db 0

key_holded db 0


kemp_var db 0

cur_keys db 0

kb_buf ds 8

;   1 2 4 8 16

;#fe 0 cs z x c v

;#fd 1   a s d f g

;#fb 2   q w e r t

;#f7 3   1 2 3 4 5

;#ef 4   0 9 8 7 6

;#df 5   p o i u y

;#bf 6 en l k j h

;#7f 7 sp ss m n b

keys_table

db #01,"ZXCV"

db "ASDFG"

db "QWERT"

db "12345"

db "09876"

db "POIUY"

db #0d,"LKJH"

db #20,#02,"MNB",#00



KEMPSTON

;kempston (original) table by sand'mHm

           .----------.

           |    up    |

           | ???00100 |

.----------x----------x----------.

|   left   |   fire   |  right   |

| ???00010 | ???10000 | ???00001 |

'----------x----------x----------'

           |   down   |

           | ???01000 |

           '----------' 





LD B,0 ;Проверяем порт на стабильность, т.к. работа нестабильного кемпстона нежелательна. 

LOOP IN A,(31) ;#1F NOT #DF!!!!

AND A

JR NZ,KEMP_OFF

DJNZ LOOP KEMP_OFF

LD (KEMPSTON),A

. . . . KEMPSTON DEFB 0 ;Если число не равно нулю,   то кемпстон отсутствует.





;from chain reaction

CHKEMP  LD  BC,0       ; Check for kempston joystick present

CHKEM1  IN  A,(#1F)

OR  C

LD  C,A

DJNZ CHKEM1

AND  #1F

RET       ; Returns zero flag set if kempston

 




;from Sword Of Ianna

; Read routine for kempston joysticks

read_kempston_joystick:

ld c, 31

in c, (c)

ld a, 255

cp c

jr z, nokempston ; if the value read is 255, then there is no kempston interface

xor a ; clear carry and A

kempston_right rr c

jr nc, kempston_left

or $08 ; right is pressed

kempston_left rr c

jr nc, kempston_down

or $04 ; left is pressed

kempston_down rr c

jr nc, kempston_up

or $02 ; down is pressed

kempston_up rr c

jr nc, kempston_fire

or $01 ; up is pressed

kempston_fire rr c

ret nc ; no carry, just return

or $10

ret

nokempston xor a

ret ; nothing read




; Example Kempston Joystick control routine.

joycon ld bc,31            ; Kempston joystick port.

       in a,(c)            ; read input.

       and 2               ; check "left" bit.

       call nz,joyl        ; move left.

       in a,(c)            ; read input.

       and 1               ; test "right" bit.

       call nz,joyr        ; move right.

       in a,(c)            ; read input.

       and 8               ; check "up" bit.

       call nz,joyu        ; move up.

       in a,(c)            ; read input.

       and 4               ; check "down" bit.

       call nz,joyd        ; move down.

       in a,(c)            ; read input.

       and 16              ; try the fire bit.

       call nz,fire        ; fire pressed.



SINCLAIR 1 & 2

;from Sword Of Ianna

; Read routine for Sinclair 1 joysticks

read_sinclair1_joystick:

      ld bc, $effe

      in c, (c)  ; Leemos solo la fila 6-0. Los bits a 0 estan pulsados

      xor a

sinclair1_fire rr c

jr c, sinclair1_up

or $10 ; fire is pressed

sinclair1_up rr c

jr c, sinclair1_down

or $01 ; up is pressed

sinclair1_down rr c

jr c, sinclair1_right

or $02 ; down is pressed

sinclair1_right rr c

jr c, sinclair1_left

or $08 ; right is pressed

sinclair1_left rr c

ret c ; no carry, just return

or $04 ; left pressed

ret





;from Sword Of Ianna

; Read routine for Sinclair 2 joysticks

read_sinclair2_joystick:

      ld bc, $f7fe

      in c, (c)  ; Leemos solo la fila 1-5. Los bits a 0 estan pulsados

      xor a

sinclair2_left rr c

jr c, sinclair2_right

or $04 ; left is pressed

sinclair2_right rr c

jr c, sinclair2_down

or $08 ; right is pressed

sinclair2_down rr c

jr c, sinclair2_up

or $02 ; down is pressed

sinclair2_up rr c

jr c, sinclair2_fire

or $01 ; up is pressed

sinclair2_fire rr c

ret c ; no carry, just return

or $10 ; left pressed

ret


MOUSE


.---------------------x-------------------.

|KEMPSTON MOUSE port: |  AMX MOUSE ports: |

|---------------------x-------------------|

|   buttons= #FADF    |  #1F(31) Data A   |

|   X-AXIS = #FBDF    |  #3F(63) Data B   |

|   Y-AXIS = #FFDF    |  #DF mouse button |

'---------------------x-------------------'




(c) Alexander Nikiforov

   Hi world! Решил я тут немного рассказать о драйвере поддержки

kempston mouse. Эта идея возникла у меня после просмотра  газеты

BODY, в которой SATSOFT никак не может (или не хочет ?)  сделать

нормально работающий драйвер мышки.

   Kempston mouse подключается к порту #DF:

#FADF - состояние кнопок мышки:

bit 0 - левая кнопка,

bit 1 - правая кнопка,

bit 2 - средняя кнопка.

#FBDF - X-координата мышки

#FFDF - Y-координата мышки

   Состояние портов #FBDF и #FFDF само меняется при движении мы-

ши. Старшие пять битов порта #FADF не используются и установлены

в еденицу,  а три младших бита показывают состояние кнопок:  0 -

кнопка не нажата, 1 - нажата. Но с кнопками не все  так  просто.

Дело в том, что в мышках,  которые продает в Минске Stinger,  не

используется третья кнопка. Поэтому, если Вы собираетесь исполь-

зовать в программе третью кнопку, то продублируйте  ее  нажатием

двух крайних кнопок одновременно (т.е. одновременное нажатие ле-

вой и правой кнопки должно вызывать то же действие, что и третья

кнопка). Так же не существует единого мнения какой бит (0 или 1)

должен отвечать за левую кнопку, а какой за правую. У меня нуле-

вой бит - это левая кнопка   (поддерживает FUT и ZX-WORD),  а  у

кого-то возможно это правая кнопка (поддерживает  НЛО 2,  ЧЕРНЫЙ

ВОРОН (демо), MINESWEEPER). Поэтому рекомендуется делать драйвер

с автоопределением кнопки fire, т.е. первая нажатая кнопка  ста-

новится выстрелом (так сделано в ZX-FORMAT, ZxNews и в этом  но-

мере LPRINT'а). Или делайте обе кнопки выстрелом (MOVE,  ENERGY,

старые номера LPRINT /с моим viewer'ом/).

   Ну а теперь собственно драйвер с небольшими комментариями.


   Программа проверки наличия kempston mouse.  Если мышки у  Вас

нет то из всех трех портов должно считываться  одинаковое  число

(обычно 0 или 255). Если же мыша у Вас есть, то вероятность сов-

падения всех трех чисел практически равна нулю (единственный ва-

риант когда она высока - это если Вы включили комп в сеть и  еще

ни разу не касались мышки, в этом случае из всех трех портов бу-

дет считываться число 255). Правда возможен такой вариант, что у

Вашей машины нестабильная шина данных и тогда со всех трех  пор-

тов считаются разные числа. Для этого случая я проверяю  старшие

пять битов порта кнопок #FADF и если они не равны еденице,  зна-

чит мыша отсутствует. Также в Питерских программах перед провер-

кой наличия мышки в порты  #5F и #7F  заносится байт #90.  Зачем

это нужно я не знаю, но на всякий случай советую  выполнять  эту

процедуру. Но в любом случае рекомендуется предусмотреть  ручное

отключение/включение kempston mouse.

; Проверка наличия kempston mouse 

INIT LD A,#90 ; ???

OUT (#7F),A ; ???

OUT (#5F),A ; ???

LD BC,#FADF

IN A,(C) ; Считываем в A состояние порта #FADF

PUSH AF ; и сохраняем его на стеке

AND #F8 ; Проверка старших пяти битов на 1

CP #F8

CALL NZ,NOTMOUS ; Вызов подпрограммы отключения мышки

INC B

IN L,(C) ; Считываем в L состояние порта #FBDF

LD B,#FF

IN H,(C) ; Считываем в H состояние порта #FFDF

POP AF ; Восстанавливаем регистр A

CP L ; Проверка на совпадение значений

JR NZ,$+6 ; Если байты разные, то на YESMOUSE

CP H ; Если одинаковые, то проверям дальше

CALL Z,NOTMOUS  ; Если опять сопали, то на откл. мышки

YESMOUS ... ; Kempston mouse present


   Для того чтобы после старта программы курсор появлялся в нуж-

ном месте экрана, а не черт знает где, вызывайте это подпрограм-

му (так же вызывайте ее после  ручного  включения  мышки,  чтобы

курсор не прыгал в неизвестном направлении):

; Восстановление координат

MXY LD A,#FB   ; Прочитать состоянии X-координаты

IN A,(#DF)   ; мышки и

LD (MOUSE_X+1),A  ; занести его в метку MOUSE_X

LD A,#FF   ; Прочитать состоянии Y-координаты

IN A,(#DF)   ; мышки и

LD (MOUSE_Y+1),A  ; занести его в метку MOUSE_Y

RET 


   Эта программа служит для опроса кнопок мыши.  Тут думаю все и

так понятно, но комментарии все таки поставлю.

; Проверка состояния кнопок мышки

FIRE LD A,#FA ; Чтение байта состояния кнопок

IN A,(#DF)

RRA ; Проверка нажатия левой кнопки

JP NC,L_BUT     ; Если нажата, то прейти на метку L_BUT

RRA ; Проверка нажатия правой кнопки 

JP NC,R_BUT     ; Если нажата, то прейти на метку R_BUT

... ; Кнопки не были нажаты (третья кнопка не используется)


   Ну и программка опроса движения мышки.  Я использую эту прог-

рамму так: сначала стираю курсор с экрана, вызываю программу MO-

VE, которая изменяет координаты  X  и  Y,  и печатаю курсор (Все

это происходит естественно в прерывании). В своих  программах  я

увеличиваю смещение мышки в два раза, чтобы  курсор  перемещался

быстрее. Если Вам это не надо, то уберите команды ADD A,A.

; Движение мышки. X=0-255, Y=0-191

MOVE LD A,#FB

IN A,(#DF) ; Читаем состояние X-координаты мышки

MOUSE_X LD E,0 ; В регистре E предыдущая X-координата

LD (MOUSE_X+1),A ; Сохраняем новую координату

SUB E ; Проверяем изменилась ли X-координата

CALL NZ,MOVE_X   ; Если да, то вызываем подпрог. MOVE_X

LD A,#FF

IN A,(#DF) ; Читаем состояние Y-координаты мышки

MOUSE_Y LD E,0 ; В регистре E предыдущая Y-координата

LD (MOUSE_Y+1),A ; Сохраняем новую координату

SUB E ; Проверяем изменилась ли Y-координата

CALL NZ,MOVE_Y   ; Если да, то вызываем подпрог. MOVE_Y

...

MOVE_Y JP M,MOUSE_D ; Если флаг S = 1, значит мышку двигали вниз, иначе вверх

MOUSE_U ADD A,A ; Увеличиваем смешение в два раза

LD D,A ; Сохраняем его в рег. D

LD A,(Y_COORD)   ; Берем текущую Y-координату курсора

SUB D ; Уменьшаем ее

JR NC,$+3 ; Если результат оказался меньше 0, 

XOR A ; то обнулить Y-координату

LD (Y_COORD),A   ; Заносим новую Y-координату курсора

RET

MOUSE_D NEG ; Приводим смешение в нормальный вид

ADD A,A ; Увеличиваем смешение в два раза

LD D,A ; Сохраняем его в рег. D

LD A,(Y_COORD)   ; Берем текущую Y-координату курсора

ADD A,D ; Увеличиваем ее

JR C,MM_2 ; Если результат оказался больше 255,

CP 192 ; или больше 191, то

JR C,$+4

MM_2 LD A,191         ; меняем его на 191

LD (Y_COORD),A   ; Заносим новую Y-координату курсора

RET 

MOUSE_L NEG ; Приводим смешение в нормальный вид 

ADD A,A ; Увеличиваем смешение в два раза

LD D,A ; Сохраняем его в рег. D

LD A,(X_COORD)   ; Берем текущую X-координату курсора

SUB D ; Уменьшаем ее

JR NC,$+3 ; Если результат оказался меньше 0, 

XOR A ; то обнулить X-координату

LD (X_COORD),A   ; Заносим новую X-координату курсора

RET 

MOVE_X JP M,MOUSE_L ; Если флаг S = 1, значит мышку дви гали влево, иначе вправо

MOUSE_R ADD A,A ; Увеличиваем смешение в два раза

LD D,A ; Сохраняем его в рег. D

LD A,(X_COORD)   ; Берем текущую X-координату курсора

ADD A,D ; Увеличиваем ее

JR NC,$+4 ; Если результат оказался больше 255,

LD A,255         ; то меняем его на 255

LD (X_COORD),A   ; Заносим новую X-координату курсора

RET 







; Kempston mouse driver with master/slave support. Written by Patrik Rak in 2013, and revised in 2016.

; Based on a driver originally written for testing ZXDS

updatemouse

        ld      hl,ports

        ld      de,mouseinput

        ld      c,0xDF

.loop   ld      b,(hl)

        inc     hl

        in      a,(c)

        ld      (de),a

        inc     de

        ld      a,(mouseinput+6)%256

        cp      e

        jr      nz,.loop

        ex      de,hl

        call    .pair

.pair   ld      c,255

        ld      a,(de)

        call    .update

        ld      c,191

        ld      a,(de)

        cpl

.update inc     de

        ld      b,a

        sub     (hl)

        ld      (hl),b

        inc     hl

        jp      m,.dec

        add     a,(hl)

        jr      c,.clamp

        cp      c

        jr      c,.ok

.clamp  ld      a,c

        jr      .ok

.dec    add     a,(hl)

        jr      c,.ok

        xor     a

.ok     ld      (hl),a

        inc     hl

        ret

ports   db      0xFB ; mouse 1 X port

        db      0xFF ; mouse 1 Y port

        db      0x0B ; mouse 2 X port

        db      0x0F ; mouse 2 Y port

        db      0xFA ; mouse 1 button port

        db      0x0A ; mouse 2 button port

mouseinput

mouse1x db      0

mouse1y db      0

mouse2x db      0

mouse2y db      0

mouse1b db      0 ; mouse 1 buttons

mouse2b db      0 ; mouse 2 buttons

old1x   db      0

pos1x   db      0 ; mouse 1 X

old1y   db      0

pos1y   db      0 ; mouse 1 Y

old2x   db      0

pos2x   db      0 ; mouse 2 X

old2y   db      0

pos2y   db      0 ; mouse 2 Y



;FROM VELESOFT site

KEMPSTON MOUSE - DRIVER (SHORT VERSION 1)

Driver code is optimised for short length. 

Before first run mouse driver(or after long time delay) must be called KOREKCE routine for mouse calibration.

call KOREKCE --> k-mouse calibration + set new axis values (for master k-mouse or any other kempston mouse interfaces)

                 INPUT : L=new X-axis(0-255) / H=new Y-axis(0-191) ;OUTPUT: none

call MOUSE   --> call mouse driver (for master k-mouse or any other kempston mouse interfaces)

                 INPUT : none  ;OUTPUT: A=buttons status (*) + modify mouse positions (*)

(*) - MOUSE DRIVER OUTPUT:

master k-mouse or any other kempston mouse interface:

register L or (coord+0) = 1B new x-axis window position(absolute)

register H or (coord+1) = 1B new y-axis window position(absolute)

register A or (contrb)  = 1B new mouse buttons 

(D0=right button,D1=left button,D2=middle button) - 0=inactive/1=active button

register E or (oldco+0) = 1B previous(old) x-axis window position(absolute)

register D or (oldco+1) = 1B previous(old) y-axis window position(absolute)

register BC is modified, after return from driver will contain value #FADF.

examples for your program:

before first call mouse driver (or after start game) you must set new default X-axis and Y-axis values:

LD L,new_X-axis

LD H,new_Y-axis

CALL KOREKCE

...

after long time delay (pause / start game / exit from menu) must be used this routine:

LD HL,(COORD)

CALL KOREKCE

...

mouse driver can be called each interrupt:

PUSH BC

PUSH DE

CALL MOUSE

POP DE

POP BC

JP NZ,FIRE ;press any mouse button to fire

...

...

from X and Y positions your program must calculate videoram adress for print cursor

 

;SHORT KEMPSTON MOUSE DRIVER FROM VELE

cpu  Z80UNDOC

RELAXED ON

;RUSSIAN KEMPSTON MOUSE DRIVER

XMAX     equ  255

XMIN     equ  0

YMAX     equ  191

YMIN     equ  0

;XECUTE MOUSE

;TAKE COORDINATES FROM CURPOS=

;=COORD

MOUSE    ld   hl,(COORD)

;L=X-axis ;H=Y-axis

         ld   bc,$FBDF

         ld   de,(OLDCO)

         in   a,(c)

         ld   (OLDCO),a

         sub  e

         jr   z,NM_X

         jp   p,MX_PL

         add  a,l

         jr   c,ZER_X

         xor  a

ZER_X    ld   l,a

         jr   NM_X

MX_PL    add  a,l

         jr   c,BEX_Z

         cp   XMAX

         jr   c,BEX_B

BEX_Z    ld   a,XMAX

BEX_B    ld   l,a

NM_X     ld   b,$FF

         in   a,(c)

         ld   (OLDCO+1),a

         sub  d

         jr   z,NM_Y

         neg  

         jp   p,MY_PL

         add  a,h

         jr   c,ZER_Y

         xor  a

ZER_Y    ld   h,a

         jr   NM_Y

MY_PL    add  a,h

         jr   c,BEY_Z

         cp   YMAX

         jr   c,BEY_B

BEY_Z    ld   a,YMAX

BEY_B    ld   h,a

NM_Y     ld   a,h

         cp   $FF

         jr   c,BIGY

         ld   h,$FF

BIGY     cp   YMIN

         jr   nc,SMALY

         ld   h,YMIN

SMALY    ld   a,l

         cp   $FF

         jr   c,DIRY

         ld   l,$FF

DIRY     cp   XMIN

         jr   nc,DIMENS

         ld   l,XMIN

DIMENS   ld   (COORD),hl

         ld   bc,$FADF

         in   a,(c)

         cpl  

         and  7

         ld   (CONTRB),a

         ret  

;INPUT: L=new X-axis;H=new Y-axis

KOREKCE  ld   a,$FB

         in   a,($DF)

         ld   (OLDCO),a

         ld   a,$FF

         in   a,($DF)

         ld   (OLDCO+1),a

         ld   (COORD),hl

         ret  

;new cursor position

COORD    defb 0,0

;buttons status

CONTRB   defb 0

;working (previous position)

OLDCO    defb 0,0

LENGTH   equ  $-MOUSE





;MOUSE DRIVER WITH FIRE BUTTON AUTOCONFIG (like in zx-format)

;(С) Andrey Rachkin'95

        JR       MDRV

DIRECTZ DEFB  0 ;FIRE

        DEFB  0 ;UP

        DEFB  0 ;DOWN

        DEFB  0 ;RIGHT

        DEFB  0 ;LEFT

        DEFB  0 ;CANCEL

MCOORD  DEFW  0 ;LAST CURSOR COORDS IN PIXELZ

MPORTS  DEFW  0 ;LAST READED MAUSY COORDS

NONDEF  AND   3 ;HERE COMEZ BUTTONZ CONTROL IF FIRE BUTON NOT DEFINED

        JR    Z,MDRV4 ;IF NONE BUTTON PUSHED

        CP    1

        JR    Z,NONDEF_

        XOR   A

        LD    (MDRV3+2),A

        LD    A,5

        LD    (MDRV2+2),A

NONDEF_ LD    HL,0

        LD    (MDRV1),HL

        POP   IX

;MAIN PROC OF MOUSEDRIVER

MDRV    PUSH  IX

        LD    HL,DIRECTZ

        PUSH  HL

        POP   IX

        XOR   A

        LD    (HL),A ;CLEARING

        INC   HL     ;OF

        LD    (HL),A ;DIRECTZ

        INC   HL     ;BUFER

        LD    (HL),A

        INC   HL

        LD    (HL),A

        INC   HL

        LD    (HL),A

        INC   HL

        LD    (HL),A

        INC   HL

        LD    BC,#FADF ;BUTTONZ CONTROL

        IN    A,(C)    ;READING FROM PORT OF BUTTONS

        CPL

MDRV1   JR    NONDEF ;JR UNTIL FIRE BUTTON NOT DEFINED

        RRA

MDRV2   RL    (IX+0) ;FIRE

        RRA

MDRV3   RL    (IX+5) ;CANCEL

;COORDS CONTROL

MDRV4   LD    HL,(MCOORD) ;FORM LAST CURSOR COORDS

        LD    DE,(MPORTS) ;FROM LAST READED MOUSE COORDS

        LD    BC,#FBDF

        IN    A,(C) ;READING FROM PORT X-COORD (0-#FF)

        LD    (MPORTS),A

        SUB   E

        JR    Z,MDRV9

        JP    P,MDRV6

        LD    (IX+4),1 ;MOVE LEFT

        ADD   A,L

        JR    C,MDRV5

        XOR   A        ;MIN X-COORD

MDRV5   LD    L,A

        JR    MDRV9

MDRV6   ADD   A,L

        LD    (IX+3),1 ;MOVE RIGHT

        JR    C,MDRV7

        CP    #FE      ;MAX X-COORD

        JR    C,MDRV8

MDRV7   LD    A,#FE    ;MAX X-COORD

MDRV8   LD    L,A

MDRV9   LD    B,#FF

        IN    A,(C) ;READING FROM PORT Y-COORD (0-#FF)

        LD    (MPORTS+1),A

        SUB   D

        JR    Z,MDRV14

        NEG

        JP    P,MDRV11

        LD    (IX+1),1 ;MOVE UP

        ADD   A,H

        JR    C,MDRV10

        XOR   A        ;MIN Y-COORD

MDRV10  LD    H,A

        JR    MDRV14

MDRV11  ADD   A,H

        LD    (IX+2),1 ;MOVE DOWN

        JR    C,MDRV12

        CP    #BF      ;MAX Y-COORD

        JR    C,MDRV13

MDRV12  LD    A,#BF    ;MAX Y-COORD

MDRV13  LD    H,A

MDRV14  LD    (MCOORD),HL ;NEW CURSOR POSITION IN PIXELZ

        POP      IX

        RET







AMX-Mouse Port Details 

Port: Meaning:

#1F(31) Data A

#3F(63) Data B

#DF mouse button

    The AMX interface uses a Z80-PIO (programmable In/Out Interface).

    This chip is fairly complicated, and can operate in several modes.  The

    AMX interface only uses mode 1 (no connection with IM 1 by the way), so

    this is the only mode that is emulated.  The following discussion

    applies to mode 1 only, and refers to the I/O addresses as used by the

    AMX mouse interface.

    The PIO contains two 8-bit interrupt vector registers (VECA and VECB),

    two 2-bit mode registers (MODEA and MODEB), two 8-bit data registers

    (DATAA and DATAB), and two 1-bit interrupt-enable latches (IEA and

    IEB).  The data registers can be read at any time through IN ports #1F

    and #3F respectively.  (Note that #1F is also used by the Kempston

    joystick interface, and also by the Disciple interface, and that #3F is

    also used by the Multiface.)  The other registers cannot be read, only

    set, through the write-only control register CTRLA and CTRLB, accessed

    via OUT port #5F and #7F respectively:


       Bit    7   6   5   4   3   2   1   0

      WRITE |      interrupt vector     | 0 |     Set interrupt vector


       Bit    7   6   5   4   3   2   1   0

      WRITE | IE | x | x | x | 0 | x | x | 1 |    Set IE latch

            

       Bit    7   6   5   4   3   2   1   0

      WRITE | MODE  | x | x | 1 | 1 | 1 | 1 |     Set PIO mode

  

  If IEA or IEB are set (1), the corresponding interrupt sequences will

    generate interrupts when prompted by the hardware, and put the

    corresponding interrupt vector on the data bus at interrupt time.  In

    Z80 Interrupt mode 2, this will then be used as a vector.  Note that

    bit 0 of the vector byte is always 0.  Z80 only emulates PIO mode 1

    behaviour (bit 7=0, bit 6=1 when setting PIO mode), and won't do

    anything if the mode is set differently.

    The AMX interface generates an A interrupt for each 'mickey' the mouse

    produces in the X direction, and a B interrupt for each mickey in the

    Y direction.  The sign of the direction is stored as a 0 (positive) or

    1 (negative) in the respective data registers.

    One additional port, IN port #DF, reads out the mouse button status.

    It returns #FF or #00.

    The AMX is emulated as follows.  At every 50 Hz frame, the mouse status

    is checked, and a PIO interrupt is emulated if necessary.  The IRET

    instruction is trapped, and if caught, more PIO interrupts are emulated

    if necessary, after actually executing the IRET, with a maximum of 32

    interrupts per frame per coordinate.  Care is taken to execute the 50

    Hz frame interrupt as well.  This latter interrupt can be distinguished

    from PIO interrupts by the fact that it puts a #FF on the bus.  It was

    also necessary to take care of cases where the first instruction

    executed after a RETI was a HALT, which is then skipped.  It seems

    that the PIO leaves the Z80 in peace for a few clock cycles after

    seeing a RETI, before generating another interrupt.

    A reset signal causes the IE latches of the PIO to reset.



Системное...

RAM 48/128/+3/...

128,+2 ;from WOS

The additional memory features of the 128K/+2 are controlled to by writes to port 0x7ffd.

As normal on Sinclair hardware, the port address is in fact only partially decoded 

and the hardware will respond to any port address with bits 1 and 15 reset. 

However, 0x7ffd should be used if at all possible to avoid conflicts with other hardware.

When memory is being paged, interrupts should be disabled and the stack should be 

in an area which is not going to change. If normal interrupt code is to run, then the

system variable at 0x5B5C (23388) must be kept updated with the last value sent 

to port 0x7FFD. Reading from 0x7ffd produces no special results: floating bus values 

will be returned as would be returned from any other port not attached to any hardware.

The byte output will be interpreted as follows:

Bits 0-2: RAM page (0-7) to map into memory at 0xc000.

Bit 3: Select normal (0) or shadow (1) screen to be displayed. The normal screen is in bank 5,

whilst the shadow screen is in bank 7. Note that this does not affect the 

memory between 0x4000 and 0x7fff, which is always bank 5.

Bit 4: ROM select. ROM 0 is the 128k editor and menu system; ROM 1 contains 48K BASIC.

Bit 5: If set, memory paging will be disabled and further output to

this port will be ignored until the computer is reset.


The memory map of these computers is:

0xffff +--------+--------+--------+--------+--------+--------+--------+--------+

       | Bank 0 | Bank 1 | Bank 2 | Bank 3 | Bank 4 | Bank 5 | Bank 6 | Bank 7 |

       |        |        |(also at|        |        |(also at|        |        |

       |        |        | 0x8000)|        |        | 0x4000)|        |        |

       |        |        |        |        |        | screen |        | screen |

0xc000 +--------+--------+--------+--------+--------+--------+--------+--------+

       | Bank 2 |        Any one of these pages may be switched in.

       |        |

       |        |

       |        |

0x8000 +--------+

       | Bank 5 |

       |        |

       |        |

       | screen |

0x4000 +--------+--------+

       | ROM 0  | ROM 1  | Either ROM may be switched in.

       |        |        |

       |        |        |

       |        |        |

0x0000 +--------+--------+

RAM banks 1,3,4,6 and most of 7 are used for the silicon disc

; the rest of 7 contains editor scratchpads.


An example of a typical bank switch on the 128 is:

     LD      A,(0x5b5c)      ;Previous value of port

     AND     0xf8

     OR      4               ;Select bank 4

     LD      BC,0x7ffd

     DI

     LD      (0x5b5c),A

     OUT     (C),A

     EI

The principle is the same for all bank switching: change only the bits you need to.

Memory banks 1,3,5 and 7 are contended, which reduces the speed of memory access in 

these banks as detailed in the Contended Memory section. As on the 48K machine, Port 0xfe 

(and all other even ports) are contended. Port 0x7ffd is not contended as of itself, 

but the high byte of the port address being 0x7f causes delays. 

See the Contended Input/Output section for full details.

The contended memory timings for these machines are similar to that for the 48K machine,

except that the 6,5,4,3,2,1,0,0 pattern starts at 14361 T states after the interrupt 

as opposed to 14335, and repeats every 228 T states, as opposed to 224





+3/+2A ;from WOS

The basic principle of paging on the +2A and +3 is the same as 

for the 128K/+2. However, the +2A and +3 have four ROMs rather 

than two, and certain extra memory configurations.

Port 0x7ffd behaves in the almost exactly the same way as on 

the 128K/+2, with two exceptions:

Bit 4 is now the low bit of the ROM selection.

The partial decoding used is now slightly different: the hardware will

respond only to those port addresses with bit 1 reset, 

bit 14 set and bit 15 reset (as opposed to just bits 1 and 15 

reset on the 128K/+2).

The extra paging features of the +2A/+3 are controlled by port 0x1ffd 

(again, partial decoding applies here: the hardware will respond to all port

addresses with bit 1 reset, bit 12 set and bits 13, 14 and 15 reset). 

This port is also write-only, and its last value should be 

saved at 0x5b67 (23399).


Port 0x1FFD responds as follows:

  Bit 0: Paging mode. 0=normal, 1=special

  Bit 1: In normal mode, ignored.

  Bit 2: In normal mode, high bit of ROM selection. The four ROMs are:

          ROM 0: 128k editor, menu system and self-test program

          ROM 1: 128k syntax checker

          ROM 2: +3DOS

          ROM 3: 48 BASIC

  Bit 3: Disk motor; 1=on, 0=off

  Bit 4: Printer port strobe.

When special mode is selected, the memory map changes to one of four

 configurations specified in bits 1 and 2 of port 0x1ffd:

         Bit 2 =0    Bit 2 =0    Bit 2 =1    Bit 2 =1

         Bit 1 =0    Bit 1 =1    Bit 1 =0    Bit 1 =1

 0xffff +--------+  +--------+  +--------+  +--------+

        | Bank 3 |  | Bank 7 |  | Bank 3 |  | Bank 3 |

        |        |  |        |  |        |  |        |

        |        |  |        |  |        |  |        |

        |        |  | screen |  |        |  |        |

 0xc000 +--------+  +--------+  +--------+  +--------+

        | Bank 2 |  | Bank 6 |  | Bank 6 |  | Bank 6 |

        |        |  |        |  |        |  |        |

        |        |  |        |  |        |  |        |

        |        |  |        |  |        |  |        |

 0x8000 +--------+  +--------+  +--------+  +--------+

        | Bank 1 |  | Bank 5 |  | Bank 5 |  | Bank 7 |

        |        |  |        |  |        |  |        |

        |        |  |        |  |        |  |        |

        |        |  | screen |  | screen |  | screen |

 0x4000 +--------+  +--------+  +--------+  +--------+

        | Bank 0 |  | Bank 4 |  | Bank 4 |  | Bank 4 |

        |        |  |        |  |        |  |        |

        |        |  |        |  |        |  |        |

        |        |  |        |  |        |  |        |

 0x0000 +--------+  +--------+  +--------+  +--------+

RAM banks 1,3,4 and 6 are used for the disc cache and RAMdisc, 

while Bank 7 contains editor scratchpads and +3DOS workspace.

The contended memory timings differ on the +2A/+3 from the earlier machines

; firstly, the timing differences mean that the top-left pixel of the screen

is displayed 14364 T-states after the 50 Hz interrupt occurs, as opposed 

to 14336. The T-states (relative to the interrupt) at which delays occur 

are given in the following table:

      Cycle #    Delay

      -------    -----

       14365       1

       14366   No delay

       14367       7

       14368       6

       14369       5

       14370       4

       14371       3

       14372       2

       14373       1

       14374   No delay

       14375       7

       14376       6

and so on, until cycle 14494, when the display of the first scanline

on the screen has been completed, and no more delays are inserted until

14593 (=14365+228) when the cycle repeats. The other difference occurs 

for instructions which have multiple 'pc+1' or 'hl' entries in the breakdown

for the other machines: on the +2A/+3, these entries are combined 

into just one. This means that, for example, JR becomes pc:4,pc+1:8.

Unlike the base 128K machine, RAM banks 4, 5, 6 and 7 are contended. 

However, Port 0xfe is not; whether ports 0x7ffd and 0x1ffd are 

contended is currently unknown.









23388.  В  этой ячейке, как известно, #00 при 48К и #10, если у вас 128К

(если вы, разумеется, не изменяли это значение переключением страниц 

памяти из бейсика  или,  скажем, размещением там распаковщика).

128 to 48 basic switcher BY unknonw

        ORG     #5D40

        DI 

        LD      SP,(#5C3D) ;;23613

        POP     HL

        LD      HL,#1303

        PUSH    HL

        LD      HL,#1B76 ;7030

        PUSH    HL

        RES     4,(IY+#01)

        LD      DE,#15BE

        LD      HL,(#5CF4) ;23631

        LD      BC,#000F

        ADD     HL,BC

        EX      DE,HL

        LD      C,#04

        LDIR 

        EI 

        RET 




; switch to 48k basic

;by unknown

      ld  sp, (#5C3D)    ; ERR_SP ;23613

      ld  hl, #1303    ; MAIN_4

      ex  (sp), hl

      res  4, (iy+1)

      ld  bc, #7FFD

      ld  a, _7FFD_ROM48K+_7FFD_MAINSCR+0

      out  (c), a

      ei

      jp  #1B76      ; STMT_RET




;Процедура Robusa 128 to 48 basic

DI

 LD HL,10072

 LD IY,23610

 EXX

     LD A,63

     LD I,A

     IM 1

     LD SP,(23613) ;5c3d

LD A,16

OUT (253),A

  RES 4,(IY+1)

   CALL 81

   CALL 7030

   JP 4867




Процедура Wlodek Blacka 128 to 48 basic

  CALL usr0

usr0 DI

LD SP,(23613) ;5c3d

POP HL

LD HL,4867

PUSH HL

LD HL,7030 ;1b76=stmt-ret

PUSH HL

RES 4,(IY+1)

LD DE,5566 ;15be

LD HL,(23631) ;5c4f

LD BC,15

ADD HL,BC

EX DE,HL

LD C,4

LDIR

 EI

 RET





Как включить 48-ой Basic

        со 128-ой памятью?

Это по правде делается очень просто:


 ;by Basil 

        DI

        LD IY,23610 ;5c3a

        LD HL,4867 ;1303 ;ПОДКЛЮЧЕНИЕ

          PUSH HL ;48-ГО

          LD (23613),SP ;5c3d

          LD HL,(23631) ;5c4f

          LD DE,15 ;f

          ADD HL,DE

          LD DE,5566 ;15be ;BASIC'А

          EX DE,HL

          LD BC,4

          LDIR         ;СО 128-ОЙ

          RES 4,(IY+1)

          LD HL,7030 ;1b76=stmt-ret

          PUSH HL      ;ПАМЯТЬЮ

        IM 1

        EI

        LD HL,10072

        EXX

        RET



Желательно выполнить корректную установку им1




;процка из моего ленточного лоадера

ADRCODES  EQU 23840  

        LD HL,MCBEG-$           ; starting from BASIC

        LD SP,STACK             ;

        DI                      ;

        RES 4,(IY+1)            ;

        ADD HL,BC               ;

        PUSH HL                 ;

                                ;

        LD HL,(23613)           ; SET

        LD DE,#1303             ;     48K

        LD (HL),E               ;         BASIC

        INC HL                  ;               IF

        LD (HL),D               ;            128K

        LD A,#10                ;       TAPE LOADER

        LD BC,#7FFD             ;

        OUT (C),A               ;

                                ;

        POP HL                  ;

        LD DE,ADRCODES          ;

        LD BC,MCEND-MCBEG       ;

        PUSH DE                 ;

        JP #33C3                ;    GO TO MAIN CODES (MCBEG)

MCBEG DISP ADRCODES

---yar kodes here

        ENT 

MCEND













 Определения  наличия   медленой  памяти

(SLOW RAM) т.е. тип машины  -  однополевая

или двухполевая.

LD A,3  ; счетчик холостых прерыв.

LD HL,INT_1 ; адрес возврата

LD (#FDFE),HL

LD BC,0 ; обнуляем счетчик

EI

HALT

HALT

LOOP_1  LD HL,(#4000) ; работа с SLOW RAM

LD (#4000),HL

INC BC

JR LOOP

INT_1   DEC A

EI

RET NZ ; возврат из холостого INT

DI

POP HL ; востанавливаем стек

PUSH BC ; сохраняем результат

LD HL,INT_2 ; адрес возврата

LD (#FDFE),HL

LD A,3 ; счетчик холостых прерыв.

LD BC,0 ; обнуляем счетчик

EI

HALT

HALT

LOOP_2  LD HL,(#FE00) ; работа с FAST RAM

LD (#FE00),HL

INC BC

JR LOOP_2

INT_2   DEC A

EI

RET NZ ; возврат из холостого INT

DI

POP HL ; востанавливаем стек

POP HL ; восстанав. старый резул.

XOR A

SBC HL,BC ; сверяем оба результата

LD HL,ABSENT ; SLOW RAM ABSENT

JR Z,$+5

LD HL,PRESENT ; SLOW RAM PRESENT

CALL PRINT





; check 128k-48k  (c) lvd^mhm

; must reside below #c000

; on exit will remain page #10 and scr 5 turned on if was 128k mode

;OUT:   A=#00 - 128k, A=#FF - 48k

;KILL:  AF,BC,DE,HL

CHK48128

ld   hl,#c011

ld   bc,#7ffd

ld   a,#10

out  (c),a

ld   e,(hl)

ld   (hl),a

out  (c),l

ld   d,(hl)

ld   (hl),l

out  (c),a

sub  (hl)     ;0 - 128k, #FF - 48k

out  (c),l

ld   (hl),d   ;restore pages

ld   d,#10

out  (c),d

ld   (hl),e

ret







;Быстрая и короткая проверка на наличие памяти 128K.

;(C) GRAND, 8.8.2006 15:34.

LD A,#10

LD HL,#C011

LD BC,#7FFD

OUT (C),A

LD (HL),B

OUT (C),L

LD (HL),A

OUT (C),A

CP (HL)

LD (23388),A;BANKM

;на выходе флаг Z=0 - означает наличие 128K







;Проверка наличия памяти 128K и очистка

;электронного диска BASIC 128.

;(C) GRAND, 12.11.2006.

LD BC,#7FFD

LD DE,#1017

OUT (C),D

LD HL,#EBEC

LD (HL),D

OUT (C),E

LD E,19

LD (HL),0

INC HL

DEC E

JR NZ,$-4

LD L,#F7

LD (HL),#C0

OUT (C),D

LD L,#EC

LD A,(HL)

OR A

LD (23388),A;BANKM

LD (23427),HL;SFNEXT

LD H,#2B

LD A,1

LD (23429),HL;SFSPACE

LD (23431),A;

;на выходе флаг Z=0 - означает наличие 128K.

;При работе процедуры прерывания должны быть

;запрещены, а ее код НЕ должен распологаться

;в диапазоне, где устанавливаются банки

;памяти, т.е. #C000...#FFFF. Процедура оперирует

:только с системными переменными BASIC 128,

:расположенными как в бывшем буфере принтера,

;так и в банке 7, поэтому никакое изменение

;ячеек, установленных после ее работы, недопустимо!






ORG     #6000

; Активизируем ПЗУ-48, экран |1

; включаем страницу 0

LD      BC,#7ffd

LD      A,#10

OUT     (C),A

; и читаем первый байт

LD      HL,#c000

LD      A,(HL)

; изменяем его и кладём на

; то же самое место, но в странице 1

CPL

LD      E,A

LD      A,#11

OUT     (C),A

LD      (HL),E

; восстанавливаем страницу 0

LD      A,#10

OUT     (C),A

; её первый байт сравниваем

; с изменённым ранее

LD      A,(HL)

CP      E

; анализируем результат

JR      NZ,mode128

; mode 48

...

...

mode128






;48/128 test

MEMORY  ld      bc,32765

ld      hl,49152

ld      de,4113  ;#1011

out     (c),d

ld      (hl),l

out     (c),e

ld      (hl),c

out     (c),d

ld      a,(hl)

or      a

jr      z,128kb





;48/128 test

LD HL,#C000

LD BC,#7FFD

LD A,#10

OUT (C),A

LD (HL),A

INC A

OUT (C),A

LD (HL),A

DEC A

OUT (C),A

CP (HL)

LD HL,KB_128

JR Z,$+4

LD HL,KB_48

CALL PRINT




;48/128 test

ORG <#8000   ; адрес должен быть

LD BC,#7FFD  ; меньше #8000

LD A,#15

OUT (C),A

LD (LABEL+#8000),A

LABEL   RST 0 ; при 48Кб - сброс

LD A,#10     ; при 128Кб продол-

OUT (C),A    ; жаем работу







ROM 1982 PROCEDURES & CALCULATOR

Подпрограммы BASIC 48


#03B5 949   Подпрограмма BEEP

      HL=частота DE=длительность

#03F8 1016  Подпрограмма BEEP

      L=частора  E=длительность

#04C2 1218  Подпрограмма SAVE

      запись на ленту без заголовка

#04C6 1222  Подпрограмма SAVE

      тоже,с блокировкой BREAK

#0556 1366  Подпрограмма LOAD

      чтение с ленты без заголовка

      IX=адрес DE=длина

      A=#00-LOAD A=#FF-VERIFY

#0C0A 3082  Печать сообщений

      DE=адрес сообщения

      A=номер по порядку

#0D4D 3405  Восстановление постоянных

      атрибутов экрана

#0D6B 3435  Подпрограмма CLS

      Очистка экрана

#0D6E 3438  Очистка служебного

      экрана

#0DAF 3503  Подпрограмма CLS

      Очистка экрана

#0DFE 3582  Подпрограмма SCROLL

      Скролл служебного экрана

      на 1 знакоместо

#0E44 3652  Очистка служебного экрана

      B=(x-1) число строк

#0E9E 3742  Адрес строки знакогене-

      ратора  HL=адрес

      A=номер строки

#15DE 5598  Подпрограмма INKEY$

      A=символ     CY=1-установлен

#1601 5633  Определение потока

      A=номер потока

#1728 5928  Очистка стека

      Очищает установленный стек

#1835 6197  Подпрогамма LIST

      HL=адрес бейсик строки

#1A1B 6683  Печать числа на экран

      BC=(0...#270F)-число (0.9999)

      печатает в 10-тиричном исчислении

#1CAD 7341  Перевод временных

      атрибутов в постоянные

#1F3D 7997  Подпрограмма PAUSE

      BC=время паузы как в бейсике

#1F54 8020  Подпрограмма BREAK

      CY=1-установлен при нажатии

#203C 8252  Подпрограмма PRINT

      DE=адрес BC=длина

      Задаются также PAPER,INK,BORDER

      AT,TAB,OVER,BRIGHT,FLASH и

      другие управляющие символы

      DB 22,0,0,16,1,17,2,"O.К."-пример

#229B 8859  Подпрограмма BORDER

      A=цвет бордюра как и в бейсике

#22B0 8880  Вычисление адреса экрана

      A=позиция по X

      C=позиция по Y

      Выходные данные

      HL=адрес экрана

      A=смещение (бит)

#22E5 8933  Подпрограмма PLOT

      B=позиция по X

      C=позиция по Y

#2314 8980  Вывод числа из стека

      A=целое число

#232D 9005  Подпрограмма CIRCLE

      Стек=радиус  <-вершина стека

      Стек=позиция по X

      Стек=позиция по Y

#2394 9108  Подпрограмма LINE

      Стек=длина   <-вершина стека

      Стек=позиция по X

      Стек=позиция по Y

#23BA 9146  Подпрограмма LINE

      B=позиция по X

      C=позиция по Y

      DE=смещение (#00=1 или #FF=-1)

#24B7 9399  Подпрограмма LINE

      Стек=длина   <-вершина стека

      Стек=позиция по X

      Стек=позиция по Y

#24BA 9402  Подпрограмма LINE

      B=позиция по X

      C=позиция по Y

      DE=смещение (#00=1 или #FF=-1)

#2AB6 10934 Ввод числа на стек

      A,E,D,C,B=>стек

#2BF1 11249 Вывод числа из стека

      A,E,D,C,B=>стек

#2CB8 11448 Ввод числа на стек из строки

      PRINT "......"=бейсик строка

      A=длина строки

#2D28 11560 Ввод числа на стек

      A=число

#2D2B 11563 Ввод числа на стек

      BC=число

#2D2E 11566 Ввод числа на стек

      BC=число

#2DA2 11682 Вывод числа из стека

      BC=целое число

#2DE3 11747 Печать числа из стека

      На стеке должно быть число,после

      печати числа,оно исчезает со стека

#30A9 12457 Умножение двух чисел

      DE*HL=HL

#33A9 13225 Проверка на свободную память

      в стеке


      RST #08

      DB (0...N)-обработка ошибок


      RST #10

      A=символ-печать символа и

      кодов управления


      RST #18

      CH_ADD=(#5C5D)-адрес символа

      для печати


      RST #20

      CH_ADD=(#5C5D)-адрес строки

      для вывода на экран


      RST #28

      DB 5,3,15....-выполняемые действия

      DB 56-конец работы калькулятора

      Работает с числами находящимися

      в данный момент на стеке


      RST #30

      BC=длина-освобождает место в

      рабочей области


      RST #38-опрос клавиатуры


 





Тайники ZX Spectrum

Системные процедуры - для программирующих на ассемблере 

Z80 ПЗУ (ROM) ZX Spectrum является ценным хранилищем готовых и 

отлаженных процедур в машинном коде.


                    8. Системные процедуры

     Для программирующих  на ассемблере  Z80 ПЗУ  (ROM) ZX Spectrum

является ценным хранилищем готовых и отлаженных процедур в машинном

коде.  Некоторые  из  них  могут  с успехом использоваться с уровня

BASIC,  создавая  дополнительные  возможности,  недоступные  другим

способом. Мы ограничимся описанием важнейших системных процедур.

                     8.1 Работа со звуком

     В ZX Spectrum динамик можно возбудить двумя способами:

BEEPER             #0385 (949)

                 - Эта процедура требует двух параметров. В регист-

                   ре DE помещается время продолжительности  звука,

                   а в  регистре HL  - частота.  Эти значения перед

                   помещением их в регистры требуют предварительных

                   преобразований.  Допустим, мы хотим получить тон

                   с частотой  f продолжительностью  t. Тогда  в DE

                   загружается   f*t,    а   в    HL    загружается

                   437500/f-30.125. Избежать  этих расчетов  можно,

                   если вызвать другую процедуру.

BEEP               #03F8 (1016)

                 - Она требует задания времени и  частоты  звука  в

                   нормальных  единицах  измерения.  Эти  параметры

                   передаются  BEEP  путем  их  помещения  в   стек

                   калькулятора.    BEEP    снимает    их    оттуда

                   самостоятельно,      выполняет       необходимые

                   преобразования и вызывает BEEPER.

     BEEPER  немного  лучше  инструкции  ZX-BASIC  BEEP с той точки

зрения, что  на него  не распространяются  ограничения на  значения

параметров.   Данные   контролируются   во   время  преобразований,

выполняемых  BEEP.   Во  время   генерации  звука   все  прерывания

запрещены.  Последней  инструкцией  BEEPER  перед  выполнением  RET

является EL.

                   8.2 Работа с магнитофоном

   Как заголовки, так и блоки данных записываются на кассету  одной

и той же процедурой:

SAVE_BYTES         #04C2 (1218)

                 - В регистрах DE должна находиться длина записыва-

                   емого блока,  в IX  - адрес  первого байта,  а в

                   буфере A - тип записываемого блока:

                      тип 0         - заголовок;

                      тип 255 (#FF) - блок данных.

                   В принципе,  они отличаются  тем, что  заголовку

                   предшествует  более   длительный   вступительный

                   сигнал (около 5 сек.), чем блоку данных (около 2

                   сек.).   Пользователь   может   использовать   и

                   остальные  значения  типа  между  0  и  255   на

                   обозначение   типа   блока.   Это   полезно  при

                   обозначении  разных  типов  данных в специальных

                   приложениях.     Такие     блоки    игнорируются

                   инструкцией  LOAD  при   чтении  (на  экран   не

                   выводятся  о  них  никакие  данные) и некоторыми

                   программами копирования.

   Стандартный заголовок состоит всегда из 17 байт:

         1 байт             - тип блока данных:

                              0 - программа BASIC

                              1 - числовой массив

                              2 - знаковый массив

                              3 - набор байтов.

         2-11 байты         - Название длиной  10  байт.  Допустимы

                              все коды от  0 до 255.  Если название

                              короче  10  байт,  то оно дополняется

                              пробелами.

         12-13 байты        - Длина блока, следующего за заголов-

                              ком.

         14-17 байты        - Зависит от типа набора:

                               Для типа 0:

                                14-15 байты - номер строки запуска,

                                              отказ   от    запуска

                                              сигнализируется

                                              значением   >   32767

                                              (#7FFF).

                                16-17 байты - длина самой программы

                                              без области  перемен-

                                              ных.

                               Для типов 1 и 2 используется  только

                               15 байт для задания  (однолитерного)

                               имени  записываемого  массива  (имя,

                               под  которым  он  хранился в области

                               переменных.

                               Для типа 3:

                                14-15 байты - начальный адрес памя-

                                              ти,     в     которой

                                              находился  массив   в

                                              момент записи.

     Приведенный выше формат заголовка должен сохраняться лишь  для

тех  случаев,   когда  записываемые   блоки  должны   быть  считаны

инструкцией LOAD.

   Считывание записанных наборов производится инструкцией:

LOAD_BYTES         #0556 (1566)

                 - Так же, как и для SAVE_BYTES,  перед  вызовом  в

                   пару DE заносим длину считываемого блока, в IX -

                   начальный адрес памяти для записи блока, а в A -

                   тип  считываемого   набора.  Дополнительно   еще

                   необходимо  установить  указатель  с инструкцией

                   SCF. Вызов этой процедуры с нулевым указателем C

                   позволяет  отказаться  от  верификации   данного

                   блока (аналог команды VERIFY).  Возможная ошибка

                   считывания   или   верификации   сигнализируется

                   обнулением   указателя   C   после   выхода   из

                   подпрограммы,  нормальное  выполнение  процедуры

                   сигнализируется установкой C=1.

                 8.3 Вывод на экран и печать.

     Вывод на  экран единичного  символа, код  которого находится в

буфере,  на  верхнюю  и  нижнюю  части  экрана, а также на принтер,

выполняется одной и той же процедурой. Перед ее вызовом  необходимо

открыть соответствующий канал с помощью процедуры:

CHAN_OPEN          #1601 (5633)

     В буфере  должен находиться  признак необходимого  канала: 1 -

для "K", 2 - для "S", 3  - для "P". Этот канал будет открыт  до тех

пор, пока мы его сами  не закроем. Если после этого  выдать команду

ассемблера:

         RST #10

то она будет печатать единичный символ, находящийся в буфере A,  по

выбранному каналу.

     Программа, приведенная ниже, иллюстрирует применение RST  #10.

Ее выполнение равнозначно команде: PRINT FLASH 1; AT 5,3;"X";#3;"A"

         LD        A,2         ;открыть канал "S"

         CALL      CHAN_OPEN

         LD        A,#12       ;код символа FLASH

         RST       #10

         LD        A,1         ;аргумент FLASH

         RST       #10

         LD        A,#16       ;код символа AT

         RST       #10

         LD        A,#5        ;аргументы AT

         RST       #10

         LD        A,#3

         RST       #10

         LD        A,#58       ;код "X"

         RST       #10

         LD        A,3         ;открыть канал "P"

         CALL      CHAN_OPEN

         LD        A,#41       ;код символа "A";

         RST       #10

         RET                   ;вывод из подпрограммы

     Обратим внимание, что знак "A" будет послан в буфер  принтера,

а  фактически  будет  отпечатан  на  бумаге только после заполнения

буфера, пересылки в буфер символа конца строки (13) или вызова:

COPY_BUFF          #0EC0 (3789)

Этот  пример  показывает,  что  пересылка  цепочки знаков по одному

символу может быть крайне утомительна. Проще применить процедуру:

PR_STRING          #203C (8252)

                 - Она печатает цепочку знаков, адрес которой задан

                   в регистре DE  и длиной в  BC. Перед ее  вызовом

                   необходимо открыть соответствующий канал.   Если

                   печатаемые  символы  не  уточняют  цвета, то они

                   устанавливаются на основании переменных  ATTR_T,

                   MASK_T, а также нечетных битов P_FLAG.

     Печать  чисел   более  затруднительна,   так  как   необходимо

осуществлять перевод двоичного числа в последовательность  символов

его десятичного представления.  Все необходимые вычисления и печать

выполняет процедура:

PRINT_FP           #2DE3 (11747)

                 - Она снимает со стека калькулятора  пять  байтов,

                   считая их числами в формате, принятом в  системе

                   ZX-BASIC, затем печатает  их с учетом  системных

                   переменных, определяющих цвет, положение и  т.д.

                   Способы размещения  чисел на  стеке калькулятора

                   мы оговорим далее.

     В случае  натуральных чисел  от 0  до 9999  можно использовать

более быструю процедуру:

OUT_NUM1           #1A1B (6683)

                 - Она печатает число, содержащееся в  BC,  на  4-х

                   полях,   выводя   вначале   необходимое    число

                   пробелов. ZX  Spectrum использует  эту процедуру

                   для печати номеров строк в листингах программ.

                     8.4 Экранная графика

     Для рисования на экране у нас есть аналоги процедур PLOT, DRAW

и CIRCLE:

PLOT_SUB           #22E5 (8933)

                 - Позволяет вывести на экран   единичную  точку  с

                   координатами  (X,Y).  Перед  вызовом  необходимо

                   поместить X в C и Y в B.

PIXEL_ADD          #22AA (8874)

                 - После  занесения   в  BC значений (Y,X) и вызова

                   этой  функции  мы  получаем  в регистре HL адрес

                   байта, описывающего данную точку экрана. К  тому

                   же  в   буфер  заносится   значение  X   MOD  8,

                   уточняющее,  о  каком  бите  данного  байта идет

                   речь.

DRAW_1             #2477 (9335)

                 - Снимает со стека калькулятора числа X и Y, после

                   чего рисует соответствующий отрезок.  Координаты

                   PLOT выбираются из системных переменных.

DRAW_3             #24BA (9402)

                 - Для рисования аналогичного отрезка этой процеду-

                   рой необходимо задать ABS Y -> B, ABS X-> C, SGN

                   Y -> D, SGN X -> E.

DRAW_ARC           #2394(9108)

                 - Рисует фрагменты дуг. Параметры X,Y,Z  для  этой

                   процедуры   должны   передаваться   через   стек

                   калькулятора. Z размещается наверху стека.

CIRCLE_1           #2320 (9005)

                 - Рисует полную окружность  с  центром  в  (X,Y) и

                   радиусом  Z,  все  параметры  также  должны быть

                   помещены в стек калькулятора.

Внимание!!!   Приведенные  выше процедуры модифицируют регистры  HL

(см.раздел "Ошибки системы").

     После вывода  нужного рисунка  на экран  его можно  вывести на

принтер. Аналогом инструкции COPY является процедура:

COPY               #0EAC (3756)

                 - Эта процедура  копирует  на  принтер  22 верхние

                   строки.

     Параметры  всех  вышеприведенных  процедур  подчинены  тем  же

ограничениям, что и соответствующие команды в BASIC.

               8.5 Очистка и перемещение экрана

CLS                #0D6B (3435)

                 - Основная процедура очистки экрана.  Ее  действие

                   аналогично действию директивы с тем же именем.

CLS_LOWER          #0D6E (3438)

                 - Очистка нижней части  экрана. Она  действует  на

                   всей  нижней  части  экрана  независимо  от   ее

                   текущих размеров и одновременно устанавливает ее

                   высоту  в  две  строки.  Инициализируются  также

                   системные    переменные    DF_CL    и    SPOSNL,

                   определяющие положение курсора.

CL_LINE            #0E44 (3652)

                 - Очистка, начиная с нижнего края экрана, заданно-

                   го  числа  строк.   Число  строк  содержится   в

                   регистре B.

     Перед вызовом этих процедур нужно убедиться, что нужные каналы

открыты.  Две  первые  из  них  оставляют  после  выхода  канал "K"

открытым. При  стирании экрана  цвета устанавливаются  на основании

системных переменных BORDER для нижней части экрана, а также ATTR_P

и MASK_P для верхней, после чего их содержимое копируется в  ATTR_T

и MASK_T.

CL_SC_ALL          #0DFE (3582)

                 - Перемещает содержимое всего  экрана  (24  полные

                   строки)  на  одну  строку  вверх (верхняя строка

                   исчезает). Ее  можно вызвать  непосредственно из

                   BASIC, так как она не требует параметров.

CL_SCROLL          #0E00 (3584)

                 - После занесения в регистр B числа  строк-1   для

                   сдвига (не менее  2) эта процедура  продвинет на

                   одну  строку  столько  строк,  сколько хотим, не

                   трогая расположенных выше. Следовательно,  можно

                   иметь вверху экрана  рисунок или текст,  которые

                   не уничтожаются при сдвиге на одну строку.

     Применяя  эти   процедуры  надо   помнить,  что   вверх  будут

подниматься также обязательные атрибуты в нижней части экрана.

                  8.6 Считывание с клавиатуры

     Информацию  с  клавиатуры  выгоднее  всего снимать с системной

переменной LAST_K. Проверяя состояние пятого бита переменной  FLAGS

можно определить:  нажата или не нажата очередная клавиша (1-нажата

новая, 0-еще  не нажата  ни одна  с момента  обнуления этого бита).

После считывания   кода клавиши, обнуляя этот  бит, мы обеспечиваем

себе возможность знать, модифицировалось ли  еще содержимое  LAST_K

или  нет.  Действует  это  только  при  включенных  замаскированных

прерываниях  в  режиме  1.  Именно   в  этом  случае  ZX   Spectrum

автоматически вызывает процедуру 50 раз в секунду:

KEYBOARD           #02BF (703)

                 - Она просматривает всю  клавиатуру, идентифицируя

                   верную комбинацию клавиш, заносит считанный  код

                   в  буфер,   а  также   в  переменную   LAST_K  и

                   устанавливает 5 бит FLAGS. Интерпретация нажатой

                   клавиши  будет   зависеть  от   трех   системных

                   переменных. Сначала проверяется содержимое MODE,

                   рассматриваемое  как  число  со  знаком  в  коде

                   дополнения до двух:

                   MODE-1 < 0   обозначает курсор K, L или C

                   MODE-1 = 0   обозначает курсор E

                   MODE-1 > 0   обозначает курсор G

                   Курсор K от L и C отличается с помощью  третьего

                   бита  переменной  FLAGS.  0  означает  K,  а   1

                   сигнализирует  L   или  C.   Третий  бит   FLAGS

                   окончательно определяет, является ли курсор L(0)

                   или  C(1).  Эта  процедура  не  ожидает  нажатия

                   клавиши.

     Чтобы  независимо  от  курсора  установить,  была  ли   нажата

конкретная клавиша, более продуктивным может быть  непосредственный

опрос клавиатуры с  помощью инструкции IN.  Здесь мы поступаем  так

же, как и в случае функции с тем же самым названием в BASIC.

                        8.7 Калькулятор

     Различные действия над числами с плавающей запятой выполняются

с помощью большого набора процедур, занимающих пространство ROM  от

#2F9B (12187) до #386D (14445). Калькулятор вызывается  инструкцией

RST #28, которая выполняет переход на адрес #335B (13457).

     Основой работы  калькулятора являются  66 различных  процедур,

выполняющих  набор   базовых  операций   на  стеке    калькулятора.

Очередность  их  выполнения  задается  цепочкой  байт,  размещенной

непосредственно за  командой RST  #28. Конец  такой цепочки  всегда

помечается байтом со значением #38 (56).

     По  необходимости  мы  ограничимся  операциями,   позволяющими

выполнять различные численные расчеты. Допустим, что наверху  стека

находятся числа ...Z,X,Y.

----------------------------------------------------------------

|Значение байта|                  |     Состояние стека        |

|--------------|     Операция     |           после            |

| Дес. | Шест. |                  |         операции           |

|--------------------------------------------------------------|

|    1 |  #01  | Замена элементов |  ... Z     Y      X        |

|    3 |  #03  | Вычитание        |        ... Z     X-Y       |

|    4 |  #04  | Умножение        |        ... Z     X*Y       |

|    5 |  #05  | Деление          |        ... Z     X/Y       |

|    6 |  #06  | Степень          |        ... Z     X**Y      |

|   15 |  #0F  | Сложение         |        ... Z     X+Y       |

|   27 |  #18  | Изменение знака  |  ... Z     X     -Y        |

|   31 |  #1F  | Синус            |  ... Z     X     sin Y     |

|   32 |  #20  | Косинус          |  ... Z     X     cos Y     |

|   33 |  #21  | Тангенс          |  ... Z     X     tg Y      |

|   34 |  #22  | Арксинус         |  ... Z     X     asn Y     |

|   35 |  #23  | Арккосинус       |  ... Z     X     acs Y     |

|   36 |  #24  | Арктангенс       |  ... Z     X     atg Y     |

|   37 |  #25  | Логарифм натур.  |  ... Z     X     ln Y      |

|   38 |  #26  | Экспонента       |  ... Z     X     exp Y     |

|   39 |  #27  | Целая часть числа|  ... Z     X     int Y     |

|   40 |  #28  | Корень квадратный|  ... Z     X     sqr Y     |

|   41 |  #29  | Знак числа       |  ... Z     X     sgn Y     |

|   42 |  #2A  | Абсолютная вел.  |  ... Z     X     abs Y     |

|   49 |  #31  | Копирование стека|  ... Z     X     Y   Y     |

|   50 |  #32  | N MOD M          |  ... Z  остаток   частное  |

|   52 |  #34  | Дописать в стек  |  ... Z     X     Y   D     |

|   56 |  #38  | Конец расчетов   |  ... Z     X     Y         |

|   58 |  #3A  | INT(Y+.5)        |  ... Z     X    INT(Y+.5)  |

|  160 |  #A0  | Дозапись 0       |  ... Z     X     Y   0     |

|  161 |  #A1  | Дозапись 1       |  ... Z     X     Y   1     |

|  162 |  #A2  | Дозапись 0.5     |  ... Z     X     Y  0.5    |

|  163 |  #A3  | Дозапись PI/2    |  ... Z     X     Y  PI/2   |

|  164 |  #A4  | Дозапись 10      |  ... Z     X     Y   10    |

----------------------------------------------------------------

     Как  пример  использования  калькулятора,  рассчитаем значение

1.5*SIN(X)+X**2*COS(X*PI/2).  Допустим,  что  значение  X находится

наверху стека.  Размещение за инструкцией #28 следующих байт первой

колонки вызовет:

---------------------------------------------------------

| Байт |            Состояние  стека                    |

|-------------------------------------------------------|

| #31  |   X   X                                        |

| #31  |   X   X   X                                    |

| #31  |   X   X   X   X                                |

| #A3  |   X   X   X   X   PI/2                         |

| #04  |   X   X   X   X*PI/2                           |

| #20  |   X   X   X   COS(X*PI/2)                      |

| #04  |   X   X   X*COS(X*PI/2)                        |

| #04  |   X   X*X*COS(X*PI/2)                          |

| #01  |   X*X*COS(X*PI/2)  X                           |

| #1F  |   X*X*COS(X*PI/2)  SIN(X)                      |

| #34  |   Дозапись 1.3 (5 байт)                        |

| #F1  |                                                |

---------------------------------------------------------

---------------------------------------------------------

| #26  |                                                |

| #66  |                                                |

| #66  |                                                |

| #66  |   X*X*COS(X*PI/2)  SIN(X)   1.3                |

| #04  |   X*X*COS(X*PI/2)  1.3*SIN(X)                  |

| #0F  |   X*X*COS(X*PI/2)+1.3*SIN(X)                   |

| #38  |   Конец вычислений                             |

---------------------------------------------------------

     Объяснения  требует  запись  в  стек  калькулятора  данных как

последовательности байт,  следующих непосредственно  за #34.  Байты

#F1#26#66#66#66  представляют  число  2*113*0.13,  а  не  0.13. Это

связано с интерпретацией их  как числа в укороченной  записи. Схема

действий следующая:

         - первый байт делим на #40 (64) и как значение  показателя

           степени принимается: остаток  от этого деления  плюс #50

           (если он отличен от 0) или следующий байт плюс #50 (если

           остаток равен 0);

         - целая часть от деления  (0,1,2,3)  плюс  1   определяет,

           сколько затребовано байт для мантиссы. Недостающие до  5

           байты заполняются нулями.

     В нашем примере #F1 (241),  деленное на #40 (64) дает  остаток

#31 (49),  а также  целую часть  3. Это  означает, что  показателем

степени нашего числа является  #31+#50=#81 и что затребовано  все 4

байта  мантиссы.  В  этой  системе  число  0  имеет   представление

#40#B0#00, так как #0, деленный на #40 дает остаток и целую  часть,

равные 0. Затем  становится второй байт  +#50 или #B0+#50=#00  (все

эти расчеты на отдельных байтах  ведутся по модулю 256) и  задается

лишь  первый  байт  мантиссы.  Остальные  байты  (до 5) дополняются

нулями. В свою очередь, число 10 имеет представление #40#B0#00#0A.

     Символ  #34  позволяет  размещать  числа  в тексте программ на

ассемблере.   Однако, часто  требуется поместить  в стек  параметры

инструкций  или  значения,  рассчитанные  в  программе.  Для  этого

предназначено  много  вспомогательных  процедур.  Они позволяют как

извлечение из стека значения, так и запись в него различных чисел:

STK_TO_BC          #2307 (8967)

                 - Два следующих за собой числа (в 5-байтовом пред-

                   ставлении) извлекаются  из стека  и заносятся  в

                   регистры  B  и  C.  Их  значения должны лежать в

                   диапазоне от -255  до 255. Иначе  осуществляется

                   возврат в BASIC с сообщением 8 (будет  выполнена

                   инструкция   RST   #8).   Эта   процедура  может

                   применяться  к  отрицательным  числам.  Их знаки

                   возвращаются  в   регистрах  D   и  E.    Числа,

                   извлекаемые из стека, округляются до  ближайшего

                   целого числа.

STK_TO_A           #2314 (8980)

                 - Процедура, аналогичная предыдущей,  с  тем отли-

                   чием, что из стека извлекается только одно число

                   и после округления до целого помещается в буфер.

                   Знак числа возвращается в регистре C.

STACK_FETCH        #2BF1 (11249)

                 - Эта  процедура  извлекает из стека все число (5

                   байтов) и размещает его в регистрах A, E, D,  C,

                   B.

FP_TO_BC           #20A2 (11682)

                 - Число из стека округляется  до ближайшего целого

                   числа и размещается  в регистрах BC.  Знак числа

                   определяется указателем Z (0 для отрицательных).

                   Если  значение  в  стеке  превысило максимальное

                   65535, то указатель C устанавливается в 1 и  это

                   единственная  реакция  на  ошибку  (возврата   в

                   ZX-BASIC не происходит).

SIK_STORE          #2AB6 (10934)

                 - Эта процедура  размещает  наверху стека 5 байтов

                   из  регистров  A,   E,  D,  C,   B.  Число   1.3

                   (#81#26#66#66#66)   можно    занести   в    стек

                   калькулятора     двумя     способами    (обратим

                   внимание,что второй способ позволяет  сэкономить

                   3 байта):

   LD     A,#81                        RST     #28

   LD     DE,#6626       или           DEFB    #34

   LD     BC,#6666                     DEFB    #F1

   CALL   #2AB6                        DEFB    #26

                                       DEFB    #66

                                       DEFB    #66

                                       DEFB    #66

                                       DEFB    #38

STACK_A            #2D28 (11560)

                 - Значение регистра  A  периодически  заносится  в

                   стек в 5-байтовом представлении.

STACK_RC           #2D2B (11563)

                 - Эта процедура аналогична предыдущей,  но  в стек

                   заносится число из регистров BC.

     После завершения расчетов калькулятор в регистрах HL размещает

адрес первого из  5-ти байтов, находящихся  на верхушке стека.  При

работе  с  калькулятором  необходимо  заботиться  о соответствующей

работе со  стеком. Следует  также помнить,  что процедура  PRINT_FP

снимает  со  стека  печатаемое  число.  В  конце  приводим вызов из

программы в машинном коде генератора псевдослучайных чисел:

         LD        A,#A5

         CALL      STACK_A

         RST       #28

         DEFB      #2F

         DEFB      #1D

         DEFB      #38

         RET

     Эта  программа  помещает  очередное  псевдослучайное  число  в

вершину стека и модифицирует системную переменную SEED.

            8.8 Дополнительные системные процедуры

SET_MIN            #16B0 (5808)

                 - Производит глобальную  очистку  рабочих областей

                   системы BASIC, а  также стека калькулятора.  Она

                   модифицирует  системные  переменные, указывающие

                   на  соответствующие  области  памяти.  Физически

                   затираются только те байты, в которые  заносятся

                   указатели конца области.

MAKE_ROOM          #1655 (5717)

                 - Вызывая ее, следует в регистрах  HL  дать  адрес

                   байта начала добавочного блока, а в BC -  размер

                   блока. Эта  процедура сама  устанавливает, какие

                   системные переменные должны быть модифицируемы и

                   в   случае   необходимости   модифицирует    их.

                   Следовательно,   она    допускает   вставку    в

                   существующую программу  на BASIC  или в  область

                   переменных новых операторов, переменных и т.д.

RECLAIM_2          #19E8 (8168)

                 - Обратная предыдущей функция. Здесь в HL заносит-

                   ся  адрес  первого  байта,  который   необходимо

                   убрать,  а  в  BC  -  размер  стираемого  блока.

                   Также, как  и прежде,  соответствующие системные

                   переменные будут автоматически модифицированы.

CLEAR_BUFF         #0EEF (8815)

                 - Процедура очищает буфер принтера и  модифицирует

                   связанные с ним системные переменные.

LINE_ADDR          #196E (6510)

                 - Отыскание адреса  строки  с  заданным  номером в

                   программе на BASIC. Перед вызовом в HL заносится

                   номер разыскиваемой строки. На выходе в HL имеем

                   адрес этой строки или первой с большим  номером.

                   Если  строка  с  заданным  номером  есть, то это

                   сигнализируется установкой указателя Z.

FREE_MEM           #1F1A (7962)

                 - Определение свободной памяти  в  области  BASIC,

                   т.е. между  STKEND и  RAMTOP. Регистры  HL и  BC

                   содержат  то  же   самое  отрицательное   число,

                   представленное   в   коде   дополнения   ло   2,

                   являющееся  разницей   между  STKEND+80   и   SP

                   (указатель стека процессора Z80).

BREAK_KEY          #1F54 (8020)

                 - Ее вызывают для проверки одновременного  нажатия

                   клавиш CS и BREAK. Если они нажаты, то указатель

                   C обнулен.









Spectrum ROM Memory map

Address Description

0000

THE 'START'

0008

THE 'ERROR' RESTART

0010

THE 'PRINT A CHARACTER' RESTART

0013

Unused

0018

THE 'COLLECT CHARACTER' RESTART

0020

THE 'COLLECT NEXT CHARACTER' RESTART

0025

Unused

0028

THE 'CALCULATOR' RESTART

002B

Unused

0030

THE 'MAKE BC SPACES' RESTART

0038

THE 'MASKABLE INTERRUPT' ROUTINE

0053

THE 'ERROR-2' ROUTINE

005F

Unused

0066

THE 'NON-MASKABLE INTERRUPT' ROUTINE

0074

THE 'CH-ADD+1' SUBROUTINE

007D

THE 'SKIP-OVER' SUBROUTINE

0095

THE TOKEN TABLE

0205

THE KEY TABLES

028E

THE 'KEYBOARD SCANNING' SUBROUTINE

02BF

THE 'KEYBOARD' SUBROUTINE

031E

THE 'K-TEST' SUBROUTINE

0333

THE 'KEYBOARD DECODING' SUBROUTINE

03B5

THE 'BEEPER' SUBROUTINE

03F8

THE 'BEEP' COMMAND ROUTINE

046E

THE 'SEMI-TONE' TABLE

04AA

THE 'PROGRAM NAME' SUBROUTINE (ZX81)

04C2

THE 'SA-BYTES' SUBROUTINE

053F

THE 'SA/LD-RET' SUBROUTINE

0556

THE 'LD-BYTES' SUBROUTINE

05E3

THE 'LD-EDGE-2' AND 'LD-EDGE-1' SUBROUTINES

0605

THE 'SAVE, LOAD, VERIFY and MERGE' COMMAND ROUTINES

07CB

THE 'VERIFY' CONTROL ROUTINE

0802

THE 'LOAD A DATA BLOCK' SUBROUTINE

0808

THE 'LOAD' CONTROL ROUTINE

08B6

THE 'MERGE' CONTROL ROUTINE

092C

THE 'MERGE A LINE OR A VARIABLE' SUBROUTINE

0970

THE 'SAVE' CONTROL ROUTINE

09A1

THE CASSETTE MESSAGES

09F4

THE 'PRINT-OUT' ROUTINES

0A11

THE 'CONTROL CHARACTER' TABLE

0A23

THE 'CURSOR LEFT' SUBROUTINE

0A3D

THE 'CURSOR RIGHT' SUBROUTINE

0A4F

THE 'CARRIAGE RETURN' SUBROUTINE

0A5F

THE 'PRINT COMMA' SUBROUTINE

0A69

THE 'PRINT A QUESTION MARK' SUBROUTINE

0A6D

THE 'CONTROL CHARACTERS WITH OPERANDS' ROUTINE

0AD9

PRINTABLE CHARACTER CODES

0ADC

THE 'POSITION STORE' SUBROUTINE

0B03

THE 'POSITION FETCH' SUBROUTINE

0B24

THE 'PRINT ANY CHARACTER(S)' SUBROUTINE

0BDB

THE 'SET ATTRIBUTE BYTE' SUBROUTINE

0C0A

THE 'MESSAGE PRINTING' SUBROUTINE

0C3B

THE 'PO-SAVE' SUBROUTINE

0C41

THE 'TABLE SEARCH' SUBROUTINE

0C55

THE 'TEST FOR SCROLL' SUBROUTINE

0D4D

THE 'TEMPORARY COLOUR ITEMS' SUBROUTINE

0D6B

THE 'CLS' COMMAND ROUTINE

0DAF

THE 'CLEARING THE WHOLE DISPLAY AREA' SUBROUTINE

0DD9

THE 'CL-SET' SUBROUTINE

0DFE

THE 'SCROLLING' SUBROUTINE

0E44

THE 'CLEAR LINES' SUBROUTINE

0E88

THE 'CL-ATTR' SUBROUTINE

0E9B

THE 'CL-ADDR' SUBROUTINE

0EAC

THE 'COPY' COMMAND ROUTINE

0ECD

THE 'COPY-BUFF' SUBROUTINE

0EDF

THE 'CLEAR PRINTER BUFFER' SUBROUTINE

0EF4

THE 'COPY-LINE' SUBROUTINE

0F2C

THE 'EDITOR' ROUTINES

0FA0

THE 'EDITING KEYS' TABLE

0FA9

THE 'EDIT KEY' SUBROUTINE

0FF3

THE 'CURSOR DOWN EDITING' SUBROUTINE

1007

THE 'CURSOR LEFT EDITING' SUBROUTINE

100C

THE 'CURSOR RIGHT EDITING' SUBROUTINE

1015

THE 'DELETE EDITING' SUBROUTINE

101E

THE 'ED-IGNORE' SUBROUTINE

1024

THE 'ENTER EDITING' SUBROUTINE

1031

THE 'ED-EDGE' SUBROUTINE

1059

THE 'CURSOR UP EDITING' SUBROUTINE

1076

THE 'ED-SYMBOL' SUBROUTINE

107F

THE 'ED-ERROR' SUBROUTINE

1097

THE 'CLEAR-SP' SUBROUTINE

10A8

THE 'KEYBOARD INPUT' SUBROUTINE

111D

THE 'LOWER SCREEN COPYING' SUBROUTINE

1190

THE 'SET-HL' AND 'SET-DE' SUBROUTINES

11A7

THE 'REMOVE-FP' SUBROUTINE

11B7

THE 'NEW' COMMAND ROUTINE

12A2

THE 'MAIN EXECUTION' LOOP

1391

THE REPORT MESSAGES

1539

THE COPYRIGHT MESSAGE

1555

Report G - No room for line

155D

THE 'MAIN-ADD' SUBROUTINE

15AF

THE 'INITIAL CHANNEL INFORMATION'

15C4

Report J - Invalid I/O device

15C6

THE 'INITIAL STREAM DATA'

15D4

THE 'WAIT-KEY' SUBROUTINE

15E6

THE 'INPUT-AD' SUBROUTINE

15EF

THE 'MAIN PRINTING' SUBROUTINE

1601

THE 'CHAN-OPEN' SUBROUTINE

1615

THE 'CHAN-FLAG' SUBROUTINE

162D

THE 'CHANNEL CODE LOOK-UP' TABLE

1634

THE 'CHANNEL 'K' FLAG' SUBROUTINE

1642

THE 'CHANNEL 'S' FLAG' SUBROUTINE

164D

THE 'CHANNEL 'P' FLAG' SUBROUTINE

1652

THE 'MAKE-ROOM' SUBROUTINE

1664

THE 'POINTERS' SUBROUTINE

168F

THE 'COLLECT A LINE NUMBER' SUBROUTINE

169E

THE 'RESERVE' SUBROUTINE

16B0

THE 'SET-MIN' SUBROUTINE

16D4

THE 'RECLAIM THE EDIT-LINE' SUBROUTINE

16DB

THE 'INDEXER' SUBROUTINE

16E5

THE 'CLOSE #' COMMAND ROUTINE

1701

THE 'CLOSE-2' SUBROUTINE

1716

THE 'CLOSE STREAM LOOK-UP' TABLE

171C

THE 'CLOSE STREAM' SUBROUTINE

171E

THE 'STREAM DATA' SUBROUTINE

1736

THE 'OPEN #' COMMAND ROUTINE

175D

THE 'OPEN-2' SUBROUTINE

177A

THE 'OPEN STREAM LOOK-UP' TABLE

1781

THE 'OPEN-K' SUBROUTINE

1785

THE 'OPEN-S' SUBROUTINE

1789

THE 'OPEN-P' SUBROUTINE

1793

THE 'CAT, ERASE, FORMAT and MOVE' COMMAND ROUTINES

1795

THE 'LIST and LLIST' COMMAND ROUTINES

17F5

THE 'LLIST' ENTRY POINT

17F9

THE 'LIST' ENTRY POINT

1855

THE 'PRINT A WHOLE BASIC LINE' SUBROUTINE

18B6

THE 'NUMBER' SUBROUTINE

18C1

THE 'PRINT A FLASHING CHARACTER' SUBROUTINE

18E1

THE 'PRINT THE CURSOR' SUBROUTINE

190F

THE 'LN-FETCH' SUBROUTINE

1925

THE 'PRINTING CHARACTERS IN A BASIC LINE' SUBROUTINE

196E

THE 'LINE-ADDR' SUBROUTINE

1980

THE 'COMPARE LINE NUMBERS' SUBROUTINE

1988

Unused

198B

THE 'FIND EACH STATEMENT' SUBROUTINE

19B8

THE 'NEXT-ONE' SUBROUTINE

19DD

THE 'DIFFERENCE' SUBROUTINE

19E5

THE 'RECLAIMING' SUBROUTINE

19FB

THE 'E-LINE-NO' SUBROUTINE

1A1B

THE 'REPORT AND LINE NUMBER PRINTING' SUBROUTINE

1A48

THE SYNTAX TABLES

1B17

THE 'MAIN PARSER' OF THE BASIC INTERPRETER

1B28

THE STATEMENT LOOP

1B6F

THE 'SEPARATOR' SUBROUTINE

1B76

THE 'STMT-RET' SUBROUTINE

1B8A

THE 'LINE-RUN' ENTRY POINT

1B9E

THE 'LINE-NEW' SUBROUTINE

1BB2

THE 'REM' COMMAND ROUTINE

1BB3

THE 'LINE-END' ROUTINE

1BBF

THE 'LINE-USE' ROUTINE

1BD1

THE 'NEXT-LINE' ROUTINE

1BEE

THE 'CHECK-END' SUBROUTINE

1BF4

THE 'STMT-NEXT' ROUTINE

1C01

THE 'COMMAND CLASS' TABLE

1C0D

THE 'COMMAND CLASSES - +00, +03 and +05'

1C1F

THE 'COMMAND CLASS +01' ROUTINE

1C22

THE 'VARIABLE IN ASSIGNMENT' SUBROUTINE

1C4E

THE 'COMMAND CLASS +02' ROUTINE

1C56

THE 'FETCH A VALUE' SUBROUTINE

1C6C

THE 'COMMAND CLASS +04' ROUTINE

1C79

THE 'EXPECT NUMERIC/STRING EXPRESSIONS' SUBROUTINE

1C96

THE 'SET PERMANENT COLOURS' SUBROUTINE

1CBE

THE 'COMMAND CLASS +09' ROUTINE

1CDB

THE 'COMMAND CLASS +0B' ROUTINE

1CDE

THE 'FETCH A NUMBER' SUBROUTINE

1CEE

THE 'STOP' COMMAND ROUTINE

1CF0

THE 'IF' COMMAND ROUTINE

1D03

THE 'FOR' COMMAND ROUTINE

1D86

THE 'LOOK-PROG' SUBROUTINE

1DAB

THE 'NEXT' COMMAND ROUTINE

1DDA

THE 'NEXT-LOOP' SUBROUTINE

1DEC

THE 'READ' COMMAND ROUTINE

1E27

THE 'DATA' COMMAND ROUTINE

1E39

THE 'PASS-BY' SUBROUTINE

1E42

THE 'RESTORE' COMMAND ROUTINE

1E4F

THE 'RANDOMIZE' COMMAND ROUTINE

1E5F

THE 'CONTINUE' COMMAND ROUTINE

1E67

THE 'GO TO' COMMAND ROUTINE

1E7A

THE 'OUT' COMMAND ROUTINE

1E80

THE 'POKE' COMMAND ROUTINE

1E85

THE 'TWO-PARAM' SUBROUTINE

1E94

THE 'FIND INTEGERS' SUBROUTINE

1EA1

THE 'RUN' COMMAND ROUTINE

1EAC

THE 'CLEAR' COMMAND ROUTINE

1EED

THE 'GO SUB' COMMAND ROUTINE

1F05

THE 'TEST-ROOM' SUBROUTINE

1F1A

THE 'FREE MEMORY' SUBROUTINE

1F23

THE 'RETURN' COMMAND ROUTINE

1F3A

THE 'PAUSE' COMMAND ROUTINE

1F54

THE 'BREAK-KEY' SUBROUTINE

1F60

THE 'DEF FN' COMMAND ROUTINE

1FC3

THE 'UNSTACK-Z' SUBROUTINE

1FC9

THE 'LPRINT and PRINT' COMMAND ROUTINES

1FDF

THE 'PRINT CONTROLLING' SUBROUTINE

1FF5

THE 'PRINT A CARRIAGE RETURN' SUBROUTINE

1FFC

THE 'PRINT ITEMS' SUBROUTINE

2045

THE 'END OF PRINTING' SUBROUTINE

204E

THE 'PRINT POSITION' SUBROUTINE

2070

THE 'ALTER STREAM' SUBROUTINE

2089

THE 'INPUT' COMMAND ROUTINE

21B9

THE 'IN-ASSIGN' SUBROUTINE

21D6

THE 'IN-CHAN-K' SUBROUTINE

21E1

THE 'COLOUR ITEM' ROUTINES

2294

THE 'BORDER' COMMAND ROUTINE

22AA

THE 'PIXEL ADDRESS' SUBROUTINE

22CB

THE 'POINT' SUBROUTINE

22DC

THE 'PLOT' COMMAND ROUTINE

2307

THE 'STK-TO-BC' SUBROUTINE

2314

THE 'STK-TO-A' SUBROUTINE

2320

THE 'CIRCLE' COMMAND ROUTINE

2382

THE 'DRAW' COMMAND ROUTINE

247D

THE 'INITIAL PARAMETERS' SUBROUTINE

24B7

THE 'LINE-DRAWING' SUBROUTINE

24FB

THE 'SCANNING' SUBROUTINE

250F

THE 'SCANNING QUOTES' SUBROUTINE

2522

THE 'SCANNING TWO CO-ORDINATES' SUBROUTINE

2530

THE 'SYNTAX-Z' SUBROUTINE

2535

THE 'SCANNING SCREEN$' SUBROUTINE

2580

THE 'SCANNING ATTRIBUTES' SUBROUTINE

2596

THE SCANNING FUNCTION TABLE

25AF

THE 'SCANNING UNARY PLUS' ROUTINE

25B3

THE 'SCANNING QUOTE' ROUTINE

25E8

THE 'SCANNING BRACKET' ROUTINE

25F5

THE 'SCANNING FN' ROUTINE

25F8

THE 'SCANNING RND' ROUTINE

2627

THE 'SCANNING PI' ROUTINE

2634

THE' SCANNING INKEY$' ROUTINE

2668

THE 'SCANNING SCREEN$' ROUTINE

2672

THE 'SCANNING ATTR' ROUTINE

267B

THE 'SCANNING POINT' ROUTINE

2684

THE 'SCANNING ALPHANUMERIC' ROUTINE

268D

THE 'SCANNING DECIMAL' ROUTINE

26C9

THE 'SCANNING VARIABLE' ROUTINE

2795

THE TABLE OF OPERATORS

27B0

THE TABLE OF PRIORITIES

27BD

THE 'SCANNING FUNCTION' SUBROUTINE

28AB

THE 'FUNCTION SKIPOVER' SUBROUTINE

28B2

THE 'LOOK-VARS' SUBROUTINE

2951

THE 'STACK FUNCTION ARGUMENT' SUBROUTINE

2996

THE 'STK-VAR' SUBROUTINE

2A52

THE 'SLICING' SUBROUTINE

2AB1

THE 'STK-STORE' SUBROUTINE

2ACC

THE 'INT-EXP' SUBROUTINE

2AEE

THE 'DE,(DE+1)' SUBROUTINE

2AF4

THE 'GET-HL*DE' SUBROUTINE

2AFF

THE 'LET' COMMAND ROUTINE

2BF1

THE 'STK-FETCH' SUBROUTINE

2C02

THE 'DIM' COMMAND ROUTINE

2C88

THE 'ALPHANUM' SUBROUTINE

2C8D

THE 'ALPHA' SUBROUTINE

2C9B

THE 'DECIMAL TO FLOATING POINT' SUBROUTINE

2D1B

THE 'NUMERIC' SUBROUTINE

2D22

THE 'STK-DIGIT' SUBROUTINE

2D28

THE 'STACK-A' SUBROUTINE

2D2B

THE 'STACK-BC' SUBROUTINE

2D3B

THE 'INTEGER TO FLOATING-POINT' SUBROUTINE

2D4F

THE 'E-FORMAT TO FLOATING-POINT' SUBROUTINE (offset +3C)

2D7F

THE 'INT-FETCH' SUBROUTINE

2D8C

THE 'POSITIVE-INT-STORE' SUBROUTINE

2D8E

THE 'INT-STORE' SUBROUTINE

2DA2

THE 'FLOATING-POINT TO BC' SUBROUTINE

2DC1

THE 'LOG(2^A)' SUBROUTINE

2DD5

THE 'FLOATING-POINT TO A' SUBROUTINE

2DE3

THE 'PRINT A FLOATING-POINT NUMBER' SUBROUTINE

2F8B

THE 'CA=10*A+C' SUBROUTINE

2F9B

THE 'PREPARE TO ADD' SUBROUTINE

2FBA

THE 'FETCH TWO NUMBERS' SUBROUTINE

2FDD

THE 'SHIFT ADDEND' SUBROUTINE

3004

THE 'ADD-BACK' SUBROUTINE

300F

THE 'SUBTRACTION' OPERATION (offset +03)

3014

THE 'ADDITION' OPERATION (offset +0F)

30A9

THE 'HL=HL*DE' SUBROUTINE

30C0

THE 'PREPARE TO MULTIPLY OR DIVIDE' SUBROUTINE

30CA

THE 'MULTIPLICATION' OPERATION (offset +04)

31AF

THE 'DIVISION' OPERATION (offset +05)

3214

THE 'INTEGER TRUNCATION TOWARDS ZERO' SUBROUTINE (offset +3A)

3293

THE 'RE-STACK TWO' SUBROUTINE

3297

THE 'RE-STACK' SUBROUTINE (offset +3D)

32C5

THE TABLE OF CONSTANTS

32D7

THE TABLE OF ADDRESSES

335B

THE 'CALCULATE' SUBROUTINE

33A2

THE 'SINGLE OPERATION' SUBROUTINE (offset +3B)

33A9

THE 'TEST 5-SPACES' SUBROUTINE

33B4

THE 'STACK NUMBER' SUBROUTINE

33C0

THE 'MOVE A FLOATING-POINT NUMBER' SUBROUTINE (offset +31)

33C6

THE 'STACK LITERALS' SUBROUTINE (offset +34)

33F7

THE 'SKIP CONSTANTS' SUBROUTINE

3406

THE 'MEMORY LOCATION' SUBROUTINE

340F

THE 'GET FROM MEMORY AREA' SUBROUTINE (offset +41)

341B

THE 'STACK A CONSTANT' SUBROUTINE (offset +3F)

342D

THE 'STORE IN MEMORY AREA' SUBROUTINE (offset +40)

343C

THE 'EXCHANGE' SUBROUTINE (offset +01)

3449

THE 'SERIES GENERATOR' SUBROUTINE (offset +3E)

346A

THE 'ABSOLUTE MAGNITUDE' FUNCTION (offset +2A)

346E

THE 'UNARY MINUS' OPERATION (offset +1B)

3492

THE 'SIGNUM' FUNCTION (offset +29)

34A5

THE 'IN' FUNCTION (offset +2C)

34AC

THE 'PEEK' FUNCTION (offset +2B)

34B3

THE 'USR' FUNCTION (offset +2D)

34BC

THE 'USR STRING' FUNCTION (offset +19)

34E9

THE 'TEST-ZERO' SUBROUTINE

34F9

THE 'GREATER THAN ZERO' OPERATION (offset +37)

3501

THE 'NOT' FUNCTION (offset +30)

3506

THE 'LESS THAN ZERO' OPERATION (offset +36)

350B

THE 'ZERO OR ONE' SUBROUTINE

351B

THE 'OR' OPERATION (offset +07)

3524

THE 'NUMBER AND NUMBER' OPERATION (offset +08)

352D

THE 'STRING AND NUMBER' OPERATION (offset +10)

353B

THE 'COMPARISON' OPERATIONS (offsets +09 to +0E, +11 to +16)

359C

THE 'STRING CONCATENATION' OPERATION (offset +17)

35BF

THE 'STK-PNTRS' SUBROUTINE

35C9

THE 'CHR$' FUNCTION (offset +2F)

35DE

THE 'VAL' AND 'VAL$' FUNCTIONS (offsets +18, +1D)

361F

THE 'STR$' FUNCTION (offset +2E)

3645

THE 'READ-IN' SUBROUTINE (offset +1A)

3669

THE 'CODE' FUNCTION (offset +1C)

3674

THE 'LEN' FUNCTION (offset +1E)

367A

THE 'DECREASE THE COUNTER' SUBROUTINE (offset +35)

3686

THE 'JUMP' SUBROUTINE (offset +33)

368F

THE 'JUMP ON TRUE' SUBROUTINE (offset +00)

369B

THE 'END-CALC' SUBROUTINE (offset +38)

36A0

THE 'MODULUS' SUBROUTINE (offset +32)

36AF

THE 'INT' FUNCTION (offset +27)

36C4

THE 'EXPONENTIAL' FUNCTION (offset +26)

3713

THE 'NATURAL LOGARITHM' FUNCTION (offset +25)

3783

THE 'REDUCE ARGUMENT' SUBROUTINE (offset +39)

37AA

THE 'COSINE' FUNCTION (offset +20)

37B5

THE 'SINE' FUNCTION (offset +1F)

37DA

THE 'TAN' FUNCTION (offset +21)

37E2

THE 'ARCTAN' FUNCTION (offset +24)

3833

THE 'ARCSIN' FUNCTION (offset +22)

3843

THE 'ARCCOS' FUNCTION (offset +23)

384A

THE 'SQUARE ROOT' FUNCTION (offset +28)

3851

THE 'EXPONENTIATION' OPERATION (offset +06)

386E

Unused

3D00

Character set

5C00

KSTATE - Used in reading the keyboard

5C08

LAST-K - Last key pressed

5C09

REPDEL - Time that a key must be held down before it repeats

5C0A

REPPER - Delay between successive repeats of a key held down

5C0B

DEFADD - Address of arguments of user defined function

5C0D

K-DATA - Second byte of colour controls entered from keyboard

5C0E

TVDATA - Colour, AT and TAB controls going to television

5C10

STRMS - Addresses of channels attached to streams

5C36

CHARS - 256 less than address of character set

5C38

RASP - Length of warning buzz

5C39

PIP - Length of keyboard click

5C3A

ERR-NR - One less than the error report code

5C3B

FLAGS - Various flags to control the BASIC system

5C3C

TV-FLAG - Flags associated with the television

5C3D

ERR-SP - Address of item on machine stack to use as error return

5C3F

LIST-SP - Return address from automatic listing

5C41

MODE - Specifies K, L, C, E or G cursor

5C42

NEWPPC - Line to be jumped to

5C44

NSPPC - Statement number in line to be jumped to

5C45

PPC - Line number of statement being executed

5C47

SUBPPC - Number within line of statement being executed

5C48

BORDCR - Border colour

5C49

E-PPC - Number of current line

5C4B

VARS - Address of variables

5C4D

DEST - Address of variable in assignment

5C4F

CHANS - Address of channel data

5C51

CURCHL - Address of information used for input and output

5C53

PROG - Address of BASIC program

5C55

NXTLIN - Address of next line in program

5C57

DATADD - Address of terminator of last DATA item

5C59

E-LINE - Address of command being typed in

5C5B

K-CUR - Address of cursor

5C5D

CH-ADD - Address of the next character to be interpreted

5C5F

X-PTR - Address of the character after the '?' marker

5C61

WORKSP - Address of temporary work space

5C63

STKBOT - Address of bottom of calculator stack

5C65

STKEND - Address of start of spare space

5C67

BREG - Calculator's B register

5C68

MEM - Address of area used for calculator's memory

5C6A

FLAGS2 - More flags

5C6B

DF-SZ - The number of lines in the lower part of the screen

5C6C

S-TOP - The number of the top program line in automatic listings

5C6E

OLDPPC - Line number to which CONTINUE jumps

5C70

OSPCC - Number within line of statement to which CONTINUE jumps

5C71

FLAGX - Various flags

5C72

STRLEN - Length of string type destination in assignment

5C74

T-ADDR - Address of next item in parameter table

5C76

SEED - The seed for RND

5C78

FRAMES - Frame counter

5C7B

UDG - Address of first user defined graphic

5C7D

COORDS - Coordinates of last point plotted

5C7F

P-POSN - Column number of printer position

5C80

PR-CC - Address of next position for LPRINT to print at

5C82

ECHO-E - Column and line number of end of input buffer

5C84

DF-CC - Address in display file of PRINT position

5C86

DF-CCL - Like DF-CC for lower part of screen

5C88

S-POSN - Column and line number for PRINT position

5C8A

S-POSNL - Like S-POSN for lower part of screen

5C8C

SCR-CT - Scroll counter

5C8D

ATTR-P - Permanent current colours

5C8E

MASK-P - Used for transparent colours

5C8F

ATTR-T - Temporary current colours

5C90

MASK-T - Temporary transparent colours

5C91

P-FLAG - More flags

5C92

MEMBOT - Calculator's memory area

5CB0

NMIADD - Non-maskable interrupt address

5CB2

RAMTOP - Address of last byte of BASIC system area

5CB4

P-RAMT - Address of last byte of physical RAM

5CB6

Channel information

SYSTEM VARS 48 & 128

48k Sys Vars

Spectrum ROM System variables

Address Length Description

5C00 8 KSTATE - Used in reading the keyboard

5C08 1 LAST-K - Last key pressed

5C09 1 REPDEL - Time that a key must be held down before it repeats

5C0A 1 REPPER - Delay between successive repeats of a key held down

5C0B 2 DEFADD - Address of arguments of user defined function

5C0D 1 K-DATA - Second byte of colour controls entered from keyboard

5C0E 2 TVDATA - Colour, AT and TAB controls going to television

5C10 38 STRMS - Addresses of channels attached to streams

5C36 2 CHARS - 256 less than address of character set

5C38 1 RASP - Length of warning buzz

5C39 1 PIP - Length of keyboard click

5C3A 1 ERR-NR - One less than the error report code

5C3B 1 FLAGS - Various flags to control the BASIC system

5C3C 1 TV-FLAG - Flags associated with the television

5C3D 2 ERR-SP - Address of item on machine stack to use as error return

5C3F 2 LIST-SP - Return address from automatic listing

5C41 1 MODE - Specifies K, L, C, E or G cursor

5C42 2 NEWPPC - Line to be jumped to

5C44 1 NSPPC - Statement number in line to be jumped to

5C45 2 PPC - Line number of statement being executed

5C47 1 SUBPPC - Number within line of statement being executed

5C48 1 BORDCR - Border colour

5C49 2 E-PPC - Number of current line

5C4B 2 VARS - Address of variables

5C4D 2 DEST - Address of variable in assignment

5C4F 2 CHANS - Address of channel data

5C51 2 CURCHL - Address of information used for input and output

5C53 2 PROG - Address of BASIC program

5C55 2 NXTLIN - Address of next line in program

5C57 2 DATADD - Address of terminator of last DATA item

5C59 2 E-LINE - Address of command being typed in

5C5B 2 K-CUR - Address of cursor

5C5D 2 CH-ADD - Address of the next character to be interpreted

5C5F 2 X-PTR - Address of the character after the '?' marker

5C61 2 WORKSP - Address of temporary work space

5C63 2 STKBOT - Address of bottom of calculator stack

5C65 2 STKEND - Address of start of spare space

5C67 1 BREG - Calculator's B register

5C68 2 MEM - Address of area used for calculator's memory

5C6A 1 FLAGS2 - More flags

5C6B 1 DF-SZ - The number of lines in the lower part of the screen

5C6C 2 S-TOP - The number of the top program line in automatic listings

5C6E 2 OLDPPC - Line number to which CONTINUE jumps

5C70 1 OSPCC - Number within line of statement to which CONTINUE jumps

5C71 1 FLAGX - Various flags

5C72 2 STRLEN - Length of string type destination in assignment

5C74 2 T-ADDR - Address of next item in parameter table

5C76 2 SEED - The seed for RND

5C78 3 FRAMES - Frame counter

5C7B 2 UDG - Address of first user defined graphic

5C7D 2 COORDS - Coordinates of last point plotted

5C7F 1 P-POSN - Column number of printer position

5C80 2 PR-CC - Address of next position for LPRINT to print at

5C82 2 ECHO-E - Column and line number of end of input buffer

5C84 2 DF-CC - Address in display file of PRINT position

5C86 2 DF-CCL - Like DF-CC for lower part of screen

5C88 2 S-POSN - Column and line number for PRINT position

5C8A 2 S-POSNL - Like S-POSN for lower part of screen

5C8C 1 SCR-CT - Scroll counter

5C8D 1 ATTR-P - Permanent current colours

5C8E 1 MASK-P - Used for transparent colours

5C8F 1 ATTR-T - Temporary current colours

5C90 1 MASK-T - Temporary transparent colours

5C91 1 P-FLAG - More flags

5C92 30 MEMBOT - Calculator's memory area

5CB0 2 NMIADD - Non-maskable interrupt address

5CB2 2 RAMTOP - Address of last byte of BASIC system area

5CB4 2 P-RAMT - Address of last byte of physical RAM

5CB6 21 Channel information





128K SysVars

Spectrum ROM System variables

Address Length Description

5C00 8 KSTATE - Used in reading the keyboard

5C08 1 LAST-K - Last key pressed

5C09 1 REPDEL - Time that a key must be held down before it repeats

5C0A 1 REPPER - Delay between successive repeats of a key held down

5C0B 2 DEFADD - Address of arguments of user defined function

5C0D 1 K-DATA - Second byte of colour controls entered from keyboard

5C0E 2 TVDATA - Colour, AT and TAB controls going to television

5C10 38 STRMS - Addresses of channels attached to streams

5C36 2 CHARS - 256 less than address of character set

5C38 1 RASP - Length of warning buzz

5C39 1 PIP - Length of keyboard click

5C3A 1 ERR-NR - One less than the error report code

5C3B 1 FLAGS - Various flags to control the BASIC system

5C3C 1 TV-FLAG - Flags associated with the television

5C3D 2 ERR-SP - Address of item on machine stack to use as error return

5C3F 2 LIST-SP - Return address from automatic listing

5C41 1 MODE - Specifies K, L, C, E or G cursor

5C42 2 NEWPPC - Line to be jumped to

5C44 1 NSPPC - Statement number in line to be jumped to

5C45 2 PPC - Line number of statement being executed

5C47 1 SUBPPC - Number within line of statement being executed

5C48 1 BORDCR - Border colour

5C49 2 E-PPC - Number of current line

5C4B 2 VARS - Address of variables

5C4D 2 DEST - Address of variable in assignment

5C4F 2 CHANS - Address of channel data

5C51 2 CURCHL - Address of information used for input and output

5C53 2 PROG - Address of BASIC program

5C55 2 NXTLIN - Address of next line in program

5C57 2 DATADD - Address of terminator of last DATA item

5C59 2 E-LINE - Address of command being typed in

5C5B 2 K-CUR - Address of cursor

5C5D 2 CH-ADD - Address of the next character to be interpreted

5C5F 2 X-PTR - Address of the character after the '?' marker

5C61 2 WORKSP - Address of temporary work space

5C63 2 STKBOT - Address of bottom of calculator stack

5C65 2 STKEND - Address of start of spare space

5C67 1 BREG - Calculator's B register

5C68 2 MEM - Address of area used for calculator's memory

5C6A 1 FLAGS2 - More flags

5C6B 1 DF-SZ - The number of lines in the lower part of the screen

5C6C 2 S-TOP - The number of the top program line in automatic listings

5C6E 2 OLDPPC - Line number to which CONTINUE jumps

5C70 1 OSPCC - Number within line of statement to which CONTINUE jumps

5C71 1 FLAGX - Various flags

5C72 2 STRLEN - Length of string type destination in assignment

5C74 2 T-ADDR - Address of next item in parameter table

5C76 2 SEED - The seed for RND

5C78 3 FRAMES - Frame counter

5C7B 2 UDG - Address of first user defined graphic

5C7D 2 COORDS - Coordinates of last point plotted

5C7F 1 P-POSN - Column number of printer position

5C80 2 PR-CC - Address of next position for LPRINT to print at

5C82 2 ECHO-E - Column and line number of end of input buffer

5C84 2 DF-CC - Address in display file of PRINT position

5C86 2 DF-CCL - Like DF-CC for lower part of screen

5C88 2 S-POSN - Column and line number for PRINT position

5C8A 2 S-POSNL - Like S-POSN for lower part of screen

5C8C 1 SCR-CT - Scroll counter

5C8D 1 ATTR-P - Permanent current colours

5C8E 1 MASK-P - Used for transparent colours

5C8F 1 ATTR-T - Temporary current colours

5C90 1 MASK-T - Temporary transparent colours

5C91 1 P-FLAG - More flags

5C92 30 MEMBOT - Calculator's memory area

5CB0 2 NMIADD - Non-maskable interrupt address

5CB2 2 RAMTOP - Address of last byte of BASIC system area

5CB4 2 P-RAMT - Address of last byte of physical RAM

5CB6 21 Channel information


В  режиме 128К  в  области  с  #5B00  по    #5BFF  также

используются  системой. Там  находятся системные переменные.

Существует   некоторая  разница  между  областями  системных

переменных  в режимах  128К и  48К. В  48К все переменные по

адресу  ниже #5C00 не существуют. Вместо этого там находится

буфер  принтера, но  многие хранят там маленькие программы и

если  одна из них будет запущена в режиме 128К, то призойдет

сбой  в системе.  Поэтому большинство программ рекомендуется

запускать  из  режима  48К  (хотя  сначала  лучше  проверить

работает она или нет в 128К-режиме).


SWAP, 20 байт, #5B00, R, 23296

Подпрограмма смены страниц памяти:

PUSH AF

PUSH BC

LD BC,#7DDF

LD A,(#5B5C)

XOR #10

DI

LD (#5B5C),A

OUT (C),A

EI

POP BC

POP AF

RET


YOUNGER, 9 байт, #5B14, R, 23316

Подпрограмма обмена страниц памяти


ON_ERR, 18 байт, #5B19, R, 23325

Подпрограмма обмена страниц в случае ошибки.


P_IN, 5 байт, #5B2F, R, 23343

Подпрограмма ввода с RS232


P_OUT, 22 байта,  #5B34, R, 23348

Подпрограмма вывода в RS232


P_OUT2, 14 байт, #5B4A, R, 23370

Подпрограмма вывода 1 байта в RS232


TARGET, слово, #5B58, N, 23384

Адрес вызываемой подпрограммы из Бейсик-ПЗУ (старое ПЗУ).


RETADDR, слово, #5B5A, X, 23386

Адрес возврата в ПЗУ-128К, после вызова процедуры.


BANK_M, байт, #5B5C, X, 23388

Копия последнего байта посланного в порт #7FFD


RAMRST, байт, #5B5D, X, 23389

Здесь храниться код инструкции Z80 RST 8


RAMERR, байт,  #5B5E, N, 23390

Код ошибки Бейсик-ПЗУ.


BAUD, слово, #5B5F, , 23391

Скорость  передачи данных  для RS232. Храниться как битовый

период  в виде  Т/26. Храниться  сначала старший байт, потом

младший.


SERF, 2 байта, #5B61, N, 23393

Флаг   состояния, принимаемого   с  RS232  значения  и  само

значение.


COL, байт, #5B63, N, 23395

Текущая колонка печати на принтере


WIDTH, байт, #5B64, , 23396

Ширина страницы принтера


TVPARS, байт, #5B65, , 23397

Код параметра ожидаемого с RS232


FLAGS3, байт, #5B66, , 23398

Флаговый байт


N_STR, 10 байт, #5B67, N, 23399

Имя файла, записываемого в RAM-DISK


HD00, байт, #5B71, N, 23409

Тип файла, записываемого в RAM-DISK


HD0B, слово, #5B72, N, 23410

Длина файла, записываемого в RAM-DISK


HD0D, слово, #5B74, N, 23412

Адрес, с которого был записан в RAM-DISK


HD0F, слово, #5B76, N, 23414

    Длина  бейсик-программы, записанной  в  RAM-DISK.  Отняв

HD0F от HD0B получите размер области переменных записанных в

RAM-DISK


HD11, слово, #5B78, N, 23416

Номер  строки, с  которой будет запускаться бейсик-программа

при считывания RAM-DISKа


SC00, байт, 5B7A, N, 23418

Тип файла


SC01, слово, #5B7B, N, 23419

Длина файла


SC08, слово, #5B7D, , 23421

Начало загрузки


SC0F, слово, #5B7F, , 23423

Длина программы


OLDSP, слово, #5B81, X, 23425

Здесь  временно сохраняется  регистр  SP  при  использовании

TSTRACK.


SFNEXT, слово, #5B83, X, 23427

Указатель свободного места в директории


SFSPACE, 3 байта, #5B85, X, 23429

Размер свободной памяти RAM-DISKа


ROW01, байт, #5B88, , 23432 малая клавиатура +1 ряд

ROW02, байт, #5B89, , 23433 2+3 ряд

ROW03, байт, #5B8A, , 23434 4+5 ряд

    Недокументированные   ячейки,  назначение   их   неясно.

(Возможно,  они предпологались для обслуживания клавиатуры с

последователеной передачей данных, как в IBM PC)


SYSRET, слово #5B8B, X, 23435

Адрес возврата для процедуры ON_ERR-32767


LAST_V, 5 байт, #5B8D, , 23437

Последнее значение введенное в калькуляторе


RNLINE, слово, #5B92, , 23442

Текущая перенумеруемая строка


RNFIRST, слово, #5B94, , 23444

Новый номер первой строки при перенумерации. Хранить сначала

старший байт, потом младший.


RNSTEP, слово, #5B96, , 23446

Приращение  номера строки при перенумерации. Хранить сначала

старший байт, потом младший.


STRPI1, 8 BAJT, #5B98, N, 23448

Полоска первой карты битов


STRIP2, 8 байта, #5BA0, N, 23456

Полоска второй карты битов


TSTACK, ,,X,

    Область временного стека, начинающегося с адреса #5BFE и

растет вниз по памяти.


TRDOS COMANDS & VARS


Системные переменные TR-DOS 5.04T.

╔═════╤═══╤══════════════════════════════════════════════════╗

║Адрес│Дл.│                 Содержимое.                      ║

╟─────┼───┼──────────────────────────────────────────────────╢

║23734│ 1 │Используется, если есть ИНТЕPФЕЙС-1. Если равно   ║

║     │   │244, то область переменных не переносится, иначе  ║

║     │   │проверяется 23832.                                ║

║23735│ 11│Не используется.                                  ║

║23746│ 1 │Содержит команду RET. Используется для переключе- ║

║     │   │ния ПЗУ на бейсик.                                ║

║23747│ 5 │Не используется.                                  ║

║23752│ 1 │Тип дисковода A:                                  ║

║     │   │ бит 7=0 - дисковод 40-дорожечный.                ║

║     │   │       1 - дисковод 80-дорожечный.                ║

║     │   │ бит 1=0 - дисковод односторонний.                ║

║     │   │       1 - дисковод двухсторонний.                ║

║     │   │ бит 0=0 - использовать 80-дорожечный дисковод как║

║     │   │           40-дорожечный.                         ║

║23753│ 1 │Тип дисковода B.                                  ║

║23754│ 1 │Тип дисковода C.                                  ║

║23755│ 1 │Тип дисковода D.                                  ║

║23756│ 1 │Текущий сектор при работе с каталогом.            ║

║23757│ 1 │Если не 0, то после позиционирования будет задерж-║

║     │   │ка. Регистр состояния ВГ-93 перед проверкой дорож-║

║     │   │ки. Бит 7 регистра состояния ВГ-93 перед чтением  ║

║     │   │адресного маркера.                                ║

║23758│ 1 │Флаг операции с секторами. При 0 -чтение секторов,║

║     │   │при 255 - запись.                                 ║

║23759│ 2 │Адрес рабочей области памяти для MOVE, COPY, LIST.║

║     │   │и при обработке номера записи при выводе в файл   ║

║     │   │данных прямого доступа.                           ║

║23761│ 1 │Длина перемещаемого файла для MOVE.               ║

║23762│ 1 │Имя массива при записи / загрузке массива в виде: ║

║     │   │биты 0 - 4 - имя массива ( от "A"=1 до "Z"=26),   ║

║     │   │бит 5 - если 0, то массив числовой,               ║

║     │   │бит 6 - если 1, то массив строковый,              ║

║     │   │бит 7 - всегда 1.                                 ║

║23761│ 2 │Номер строки автостарта при записи программы на   ║

║     │   │бейсике.                                          ║

║23763│ 2 │Счетчик секторов перемещаемого файла для MOVE.    ║

║23764│ 1 │Номер стираемого файла для MOVE.                  ║

║23765│ 1 │Текущий сектор перемещаемого файла для MOVE.      ║

║23766│ 1 │Текущая дорожка перемещаемого файла для MOVE. Ко- ║

║     │   │личество дефектных секторов при форматировании и  ║

║     │   │проверке диска. Для подпрограммы сжатия строки:   ║

║     │   │если 0, то команда находится в строке программы на║

║     │   │бейсике, иначе в другом месте. Для подпрограммы   ║

║     │   │загрузки файла: если 0, то адрес загрузки и длина ║

║     │   │берутся из описателя файла, если 3, то из 23769 и ║

║     │   │23771 соответственно, иначе адрес загрузки берется║

║     │   │из 23769, а длина - из описателя файла.           ║

║23767│ 1 │Текущий сектор стираемого файла при MOVE. Количес-║

║     │   │тво дорожек при определении типа дисковода и фор- ║

║     │   │матировании.                                      ║

║23768│ 1 │Текущая дорожка стираемого файла при MOVE. Если не║

║     │   │0, то форматируемая дорожка не проверяется.       ║

║23767│ 2 │Сохраняет CH_ADD при обработке номера записи в    ║

║     │   │файле последовательного доступа. Адрес переменной ║

║     │   │длины строки для подпрограммы сжатия строки. Адрес║

║     │   │старого массива при загрузке массива. Адрес секто-║

║     │   │ра для PEEK и POKE.                               ║

║23769│ 1 │Относительный адрес записи при обработке номера   ║

║     │   │записи в файле последовательного доступа.         ║

║23770│ 1 │Номер открываемого блока файла произвольного дос- ║

║     │   │тупа при обработке номера записи. Если равно 128, ║

║     │   │то форматируются две стороны, иначе только одна.  ║

║23769│ 2 │Счетчик освобождающихся секторов для MOVE. Адрес  ║

║     │   │загрузки файла для LOAD, номер сектора для PEEK и ║

║     │   │POKE. Адрес ключевого слова для подпрограммы сжа- ║

║     │   │тия строки. Длина файла для записи при SAVE.      ║

║23771│ 1 │Номер загружаемого сектора блока файла произволь- ║

║     │   │ного доступа при обработке номера записи. Номер   ║

║     │   │первого сектора перемещаемого файла для MOVE.     ║

║23772│ 1 │номер первой дорожки перемещаемого файла для MOVE.║

║23771│ 2 │Длина файла для LOAD. Длина файла для указания в  ║

║     │   │каталоге при SAVE. Номер потока для CAT и LIST.   ║

║23773│ 8 │Имя файла или диска при форматировании.           ║

║23781│ 1 │Расширение файла.                                 ║

║23782│ 2 │Адрес загрузки файла. Адрес таблицы секторов для  ║

║     │   │форматирования.                                   ║

║23784│ 2 │Длина файла. Адрес таблицы секторов для проверки  ║

║     │   │дорожки.                                          ║

║23786│ 1 │Обьем файла в секторах.                           ║

║23787│ 1 │Номер первого сектора файла.                      ║

║23788│ 1 │Номер первой дорожки файла.                       ║

║23789│ 2 │Адрес загрузки старого файла для COPY.            ║

║23791│ 2 │Длина старого файла в байтах для COPY.            ║

║23793│ 1 │Длина старого файла в секторах для COPY.          ║

║23794│ 1 │Номер первого сектора старого файла для COPY.     ║

║23795│ 1 │Номер первой дорожки старого файла для COPY.      ║

║23796│ 1 │Номер текущего сектора для подпрограммы           ║

║     │   │загрузки / записи секторов.                       ║

║23797│ 1 │Номер текущей дорожки для подпрограммы            ║

║     │   │загрузки / записи секторов.                       ║

║23798│ 2 │Номер дисковода для операции (0 - 3).             ║

║23800│ 1 │Дисковод-источник для COPY. Если равно 255, то при║

║     │   │выводе в файл данных буфер не удаляется.          ║

║23801│ 1 │Дисковод-приемник для COPY. Номер дисковода при   ║

║     │   │выводе каталога. Признак операции с файлом: 0 -   ║

║     │   │- загрузка, 255 - верификация.                    ║

║23802│ 1 │Время перемещения головки дисковода A: (8 - 11).  ║

║23803│ 1 │То же для дисковода B:.                           ║

║23804│ 1 │То же для дисковода C:.                           ║

║23805│ 1 │То же для дисковода D:.                           ║

║23806│ 1 │Команда контроллера для подпрограммы чтения / за- ║

║     │   │писи сектора.                                     ║

║23807│ 1 │Номер сектора для подпрограммы чтения / записи    ║

║     │   │сектора.                                          ║

║23808│ 2 │Адрес сектора для подпрограммы чтения / записи    ║

║     │   │сектора.                                          ║

║23810│ 2 │Сохраняет HL для подпрограммы вызова подпрограмм  ║

║     │   │из ПЗУ бейсика и 15635.                           ║

║23812│ 2 │Сохраняет DE.                                     ║

║23814│ 1 │Число проверяемых байтов описателя файла при его  ║

║     │   │поиске.                                           ║

║23815│ 1 │Количество стертых файлов для подпрограммы стира- ║

║     │   │ния файлов.                                       ║

║23816│ 1 │Первый символ имени файла для подпрограммы стира- ║

║     │   │ния файлов.                                       ║

║23817│ 1 │тип файла данных для OPEN# ("R", "W" или "RND").  ║

║23819│ 2 │Не используется.                                  ║

║23820│ 1 │Флаг наличия буфера: 0 - есть, иначе - нет.       ║

║23821│ 1 │Номер текущего файла при копировании всего диска с║

║     │   │двумя дисководами.                                ║

║23822│ 1 │Флаг состояния рабочей области памяти. Если равно ║

║     │   │255, то рабочая область использовалась. Если равно║

║     │   │254, то подпрограмма 963 игнорирует ошибки.       ║

║23823│ 1 │Код ошибки TR-DOS. При поиске файла подпрограммой ║

║     │   │15635: 255 - файл не найден, иначе - номер файла. ║

║23824│ 1 │Флаг операции для подпрограммы загрузки / верифи- ║

║     │   │кации файла: 0 - операция с файлом, 255 - загруз- ║

║     │   │ка / верификация сектора файла, иначе - запись    ║

║     │   │сектора файла.                                    ║

║23825│ 2 │Адрес командной строки.                           ║

║23827│ 2 │Сохраняет содержимое ERR_SP для подпрограмм воз-  ║

║     │   │врата в бейсик.                                   ║

║23829│ 1 │Если 0, то на экран выводятся сообщения об ошиб-  ║

║     │   │ках, иначе не выводятся.                          ║

║23830│ 1 │Копия системного регистра.                        ║

║23831│ 1 │Если равно 170, то при вызове 15612 заставка не   ║

║     │   │выводится, иначе выводится заставка и проверяется ║

║     │   │байт по адресу 23296. Если он равен 170, то проис-║

║     │   │ходит запуск файла "boot".                        ║

║23832│ 1 │Используется, если есть ИHТЕPФЕЙС-1. Если не 0, то║

║     │   │меняются местами блоки памяти длиной 45 байтов по ║

║     │   │адресам 23747 и 23859.                            ║

║23833│ 1 │Номер дисковода по умолчанию.                     ║

║23834│ 2 │Адрес возврата из подпрограммы завеpшения.        ║

║23836│ 2 │Сохраняет SP для подпрограмм возвpата в бейсик.   ║

║23838│ 1 │Номер файла при его поиске.                       ║

║23839│ 1 │Флаг способа вызова TR-DOS. Если 0, то вызов был  ║

║     │   │из машинного кода, иначе - из бейсика. Первый сек-║

║     │   │тор файла на диске - приемнике для COPY S.        ║

║23840│ 1 │Первый сектор файла на диске-приемнике для COPY S.║

║23840│ 3 │Сохраняет 3 первых символа командной строки.      ║

║23841│ 1 │Если не 0, то идет первый проход копирования, ина-║

║     │   │че продолжение.                                   ║

║23843│ 1 │Размер доступной памяти в секторах для MOVE и     ║

║     │   │COPY.                                             ║

╚═════╧═══╧══════════════════════════════════════════════════╝

   При инициализации системы используются следующие ячейки:

╔═════╤═══╤══════════════════════════════════════════════════╗

║Адрес│Дл.│                 Содержимое.                      ║

╟─────┼───┼──────────────────────────────────────────────────╢

║23746│ 1 │Команда RET. Используется для вызова подпрограмм  ║

║     │   │из ПЗУ бейсика.                                   ║

║24320│ 2 │Сохраняет HL для подпрограммы выполнения команды  ║

║     │   │процессора в ОЗУ.                                 ║

║24322│ 14│Не используется.                                  ║

║24336│ 3 │Подпрограмма перемещения блоков памяти LDIR или   ║

║     │   │LDDR.                                             ║

║24339│237│Временный стек.                                   ║

╚═════╧═══╧══════════════════════════════════════════════════╝

   Также при инициализации системных переменных TR-DOS 20 бай-

тов  с  адреса  23698 используются для размещения подпрограммы

проверки наличия ИHТЕPФЕЙСа-1.


     Способы обращения к ПЗУ TR-DOS.


   ПЗУ  TR-DOS  является  теневым, поэтому к нему нельзя обра-

титься непосредственно при помощи CALL. Но для того, чтобы оно

было  доступно для использования, существуют адреса, при пере-

ходе на которые включается ПЗУ TR-DOS. В ПЗУ бейсика-48 в этих

адресах  находится знакогенератор, следовательно обычно управ-

ление  туда  никогда  не  передается. Внимание!!! В ПЗУ бейси-

ка-128  в  этих адресах находится программа, поэтому при вклю-

ченном  ПЗУ бейсика-128 ПЗУ TR-DOS блокируется полностью. Ниже

вы видите список точек входа, переключающих ПЗУ.


15616 - вход в командный процессор TR-DOS.

15619 - выполнение команд TR-DOS из бейсика.

15622 - подпрограмма ввода из файла данных.

15629 - подпрограмма вывода в файл данных.

15632 - подпрограмма изменения памяти.

15635 - вызов подпрограмм TR-DOS из машинного кода.

15638 - подпрограмма обработки ошибок, поступающих из ПЗУ бей-

        сика.

15663 - переход на любой адрес в ПЗУ TR-DOS.


  Как пользоваться этими точками входа.


15616 - простой вызов. Можно установить переменные 23831 и

        23296.

15619 - из бейсика:

        RANDOMIZE USR 15619:REM:<команда>

        из машинного кода:

        1) разместить в памяти командную строку в ASCII виде с

           префиксом REM:.

        2) поместить в CH_ADD адрес этой строки.

        3) CALL 15619.

        Hапример:

       LD HL,LINE    ;установка CH_ADD

       LD (23645),HL

       JP 15619      ;выполнение команды

                     ;командная строка

LINE   DEFB 234      ;REM

       DEFB ":"      ;:

       DEFB 239      ;HOAD

       DEFB 34       ;"

       DEFM "EXAMPLE";EXAMPLE

       DEFB 34       ;"

       DEFB 13       ;ENTER


15622 - открыть канал файла данных и вызвать. На выходе символ

        из файла будет в аккумуляторе.

15629 - открыть канал файла данных, поместить в A символ и

        вызвать. Внимание!!! Содержит ошибку.

15632 - просто вызвать. Проверяет 23734 и 23832 и меняет блоки

        памяти местами, если нужно.

15635 - Номер подпрограммы поместите в регистр C, остальное

        согласно таблице:


╔═════╤══════════════════════════════════════════════════════╗

║ Ком.│                   Функции                            ║

╟─────┼──────────────────────────────────────────────────────╢

║  0  │Восстановление с ожиданием INTRQ. Воспринимает BREAK. ║

║  1  │Выбор дисковода. Номер дисковода поместите в регистр  ║

║     │A. Если в переменной с временем перемещения головки   ║

║     │дисковода бит 7 включен, то определяется времЯ пеpеме-║

║     │щения головки и проверяется переменная с типом диско- ║

║     │вода. Если она не равна 255 (ошибка), то будет опреде-║

║     │лено количество дорожек дисковода. При этом предпола- ║

║     │гается, что дисковод односторонний (ошибка). Пpи воз- ║

║     │врате определяется номеp дорожки, на которой стоит го-║

║     │ловка дисковода и заносится в регистр дорожки.        ║

║  2  │Позиционирование. Логический ноль  дорожки поместите в║

║     │A. Если по адресу 23757 не 0, то после позиционирова- ║

║     │ния будет задержка. В программе есть ошибка.          ║

║  3  │Помещает содержимое аккумулятора по адресу 23807.     ║

║  4  │Помещает содержимое HL по адресу 23808.               ║

║  5  │Чтение группы секторов. В HL поместите адрес в памяти,║

║     │в D - номер первой дорожки, в E - номер первого секто-║

║     │ра, в B - количество секторов. В программе есть ошиб- ║

║     │ка.                                                   ║

║  6  │Запись группы секторов. Параметры и ошибка аналогично ║

║     │команде 5.                                            ║

║  7  │Вывод каталога. В аккумулятор поместите номер потока, ║

║     │а в 23801 поместите номер дисковода из 23798. В прог- ║

║     │рамме есть ошибка.                                    ║

║  8  │Чтение описателя файла по адресу 23773. Номер файла   ║

║     │поместите в аккумулятор.                              ║

║  9  │Запись описателя файла. Описатель разместите по адре- ║

║     │су 23773 и поместите номер файла в аккумулятор.       ║

║  10 │Поиск файла. Проверяемую часть описателя разместите с ║

║     │адреса 23773, а ее длину поместите в 23814. Если файл ║

║     │найден, то в BC, 23823 и 23838 будет его номер, иначе ║

║     │23838 не меняется, а в 23823 и BC будет 255.          ║

║  11 │Запись файла. Имя и расширение поместите с 23773, на- ║

║     │чало в памяти поместите в HL, а длину - в DE.         ║

║  12 │Запись программы на бейсике. Имя и расширение помести-║

║     │те с адреса 23773. Если расширение не "B", то файл за-║

║     │писывается как кодовый.                               ║

║  13 │Не используется.                                      ║

║  14 │Выполняет 5 функций:                                  ║

║     │ Загрузка файла: в 23801 и 23824 поместите 0, имя и   ║

║     │ расширение поместите с 23773, а также:               ║

║     │  Для бейсик - программ - больше ничего.              ║

║     │  Для файлов CODE :                                   ║

║     │   Пpи A=0 - адрес загрузки и длина берутся из катало-║

║     │             га.                                      ║

║     │   При A=3 - адрес загрузки берется из HL, длина - из ║

║     │             DE.                                      ║

║     │   Иначе   - адрес загрузки берется из HL, длина - из ║

║     │             каталога.                                ║

║     │  Для массивов - A<>0, в HL - длина тела старого мас- ║

║     │  сива или 0, если такового нет; в 23767 - адрес тела ║

║     │  старого массива в памяти, в 23762 - имя массива.    ║

║     │ Верификация файла - все как и для загрузки, только в ║

║     │ 23801 поместите 255.                                 ║

║     │ Загрузка сектора файла - в 23801 поместите 0, в      ║

║     │ 23824 - 255, в 23767 - адрес загрузки, в HL - номер  ║

║     │ сектора, в A - 3, в DE - 0 (два последних действия - ║

║     │ для обхода ошибки. Внимание!!! Не работает с файлами ║

║     │ BASIC и DATA из-за ошибки.                           ║

║     │ Верификация сектора файла - все как и при загрузке,  ║

║     │ только в 23801 поместите 255.                        ║

║     │ Запись сектора файла - в 23801 поместите 255 (для об-║

║     │ хода ошибки), в 23824 - не 0 и не 255, в A - не 0, в ║

║     │ HL - номер сектора, в 23767 - адрес в памяти.        ║

║15-17│ Не используется.                                     ║

║  18 │ Стирание файлов. Имя и расширение поместите с 23773, ║

║     │ можно обнулить 23815, тогда по окончании там будет   ║

║     │ число стертых файлов.                                ║

║  19 │ Перенос 16 байтов с адреса в HL по адресу 23873.     ║

║  20 │ Обратное 19.                                         ║

║  21 │Проверка дорожки. Физический номер дорожки поместите в║

║     │аккумулятор и выберите сторону диска. Если обнаружены ║

║     │плохие сектора, то в 23823 и BC будет 7, а в 23766 бу-║

║     │дет их количество.                                    ║

║  22 │Выбирает верхнюю сторону диска.                       ║

║  23 │Выбирает нижнюю сторону диска.                        ║

║  24 │Проверяет принадлежность диска и настраивает систему  ║

║     │на его тип. Содержит ошибку.                          ║

╚═════╧══════════════════════════════════════════════════════╝


15638 - внутренняя точка входа. Как использовать, смотрите в

        дизассемблере.

15663 - поместите на стек нужный адрес, затем JP 15663. В ка-

        честве примера привожу подпрограммы выполнения двух

        команд TR-DOS, отсутствующих в 15635.


Форматирование диска. Имя диска поместите в 23773.

        CAHL 15632   ;изменение памяти

        LD A,255     ;эта часть программы повторяет 15635

        LD (23829),A ;сообщения не печатать

        LD (23839),A ;работает машинный код

        LD (23768),A ;дорожки не проверять

        LD (23761),A ;NO DISC при чтении адресного маркера

                     ;игнорировать

        LD HL,513    ;после завершения возврат будет в бейсик

        LD (23834),HL

        LD (23836),SP;сохранение SP

        PUSH AF      ;выделение места для адреса подпрограммы

                     ;обработки ошибок

        LD HL,7901   ;адрес подпрограммы форматирования

        PUSH HL      ;помещение его на стек

        LD HL,541    ;адрес подпрограммы установки адреса об-

                     ;работки ошибок

        PUSH HL      ;помещение его на стек

        JP 15663     ;вход в ПЗУ TR-DOS


Упаковка пространства диска.

        CALL 15632   ;снова повторяем 15635

        LD A,255

        LD (23829),A

        LD (23839),A

        LD HL,513

        LD (23834),HL

        LD (23836),SP

        PUSH AF

        LD HL,5806   ;адрес подпрограммы упаковки пространства

                     ;диска

        PUSH HL      ;помещение его на стек

        LD HL,541    ;дальше как в предыдущем примере

        PUSH HL

        JP 15663

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    







Программистам - Справочник по TR-DOS.

            ТR=DОS

-----------------------------------------

   Поводом для размещения этого материала

в журнале послужило то, что я решил заме-

нить  в  оболочке журнала turbо lоаdеr на

стандартные досовские подпрограммы работы

с диском. По многочисленным просьбам тру-

дящихся, так сказать. Развелось сейчас по

сцене Спектрума всяких там винчестеров, а

также RAМ-дисковщиков... Да и молодёжь не

в состоянии иногда найти уже дефицитные в

обиходе книги типа "ZX-Sреctrum для поль-

зователей и программистов" Николая Родио-

нова, а по сему задают в письмах вопросы:

что и как надо делать и как проверить ка-

чество и т.д. и т.п.

   Да и сам я слегка запарился работать с

tr-dоs в её "чистом" виде: отвык, однако,

за несколько лет... Короче, привожу почти

полный  сборник информации по системным и

функциям tr-dоs, а также об ошибках.



   Системные переменные ТR-DОS 5.04Т.


+=====+===+==================================================+

|Адрес|Дл.|                 Coдержuиoе.                      |

|-----+---+--------------------------------------------------+

|23734| 1 |Uспoлюзyется, еслu естю UНTEPФEЙC-1. Eслu равнo   |

|     |   |244, тo oбластю переиенных не перенoсuтся, uначе  |

|     |   |прoверяется 23832.                                |

|23735| 11|Не uспoлюзyется.                                  |

|23746| 1 |Coдержuт кoиандy RET. Uспoлюзyется для переключе- |

|     |   |нuя ПЗУ на бейсuк.                                |

|23747| 5 |Не uспoлюзyется.                                  |

|23752| 1 |Tuп дuскoвoда А:                                  |

|     |   | бuт 7=О - дuскoвoд 4О-дoрoжечный.                |

|     |   |       1 - дuскoвoд 8О-дoрoжечный.                |

|     |   | бuт 1=О - дuскoвoд oднoстoрoннuй.                |

|     |   |       1 - дuскoвoд двyхстoрoннuй.                |

|     |   | бuт О=О - uспoлюзoватю 8О-дoрoжечный дuскoвoд как|

|     |   |           4О-дoрoжечный.                         |

|23753| 1 |Tuп дuскoвoда В.                                  |

|23754| 1 |Tuп дuскoвoда C.                                  |

|23755| 1 |Tuп дuскoвoда D.                                  |

|23756| 1 |Tекyцuй сектoр прu рабoте с каталoгoи.            |

|23757| 1 |Eслu не О, тo пoсле пoзuцuoнuрoванuя бyдет задерж-|

|     |   |ка. Pегuстр сoстoянuя ВГ-93 перед прoверкoй дoрoж-|

|     |   |кu. Бuт 7 регuстра сoстoянuя ВГ-93 перед чтенuеи  |

|     |   |адреснoгo иаркера.                                |

|23758| 1 |Флаг oперацuu с сектoраиu. Прu О -чтенuе сектoрoв,|

|     |   |прu 255 - запuсю.                                 |

|23759| 2 |Адрес рабoчей oбластu паиятu для МОVE, CОPY, LIST.|

|     |   |u прu oбрабoтке нoиера запuсu прu вывoде в файл   |

|     |   |данных пряиoгo дoстyпа.                           |

|23761| 1 |Длuна переиецаеиoгo файла для МОVE.               |

|23762| 1 |Uия иассuва прu запuсu / загрyзке иассuва в вuде: |

|     |   |бuты О - 4 - uия иассuва ( oт "А"=1 дo "Z"=26),   |

|     |   |бuт 5 - еслu О, тo иассuв чuслoвoй,               |

|     |   |бuт 6 - еслu 1, тo иассuв стрoкoвый,              |

|     |   |бuт 7 - всегда 1.                                 |

|23761| 2 |Нoиер стрoкu автoстарта прu запuсu прoграииы на   |

|     |   |бейсuке.                                          |

|23763| 2 |Cчетчuк сектoрoв переиецаеиoгo файла для МОVE.    |

|23764| 1 |Нoиер стuраеиoгo файла для МОVE.                  |

|23765| 1 |Tекyцuй сектoр переиецаеиoгo файла для МОVE.      |

|23766| 1 |Tекyцая дoрoжка переиецаеиoгo файла для МОVE. Ko- |

|     |   |лuчествo дефектных сектoрoв прu фoриатuрoванuu u  |

|     |   |прoверке дuска. Для пoдпрoграииы сжатuя стрoкu:   |

|     |   |еслu О, тo кoианда нахoдuтся в стрoке прoграииы на|

|     |   |бейсuке, uначе в дрyгoи иесте. Для пoдпрoграииы   |

|     |   |загрyзкu файла: еслu О, тo адрес загрyзкu u длuна |

|     |   |берyтся uз oпuсателя файла, еслu 3, тo uз 23769 u |

|     |   |23771 сooтветственнo, uначе адрес загрyзкu берется|

|     |   |uз 23769, а длuна - uз oпuсателя файла.           |

|23767| 1 |Tекyцuй сектoр стuраеиoгo файла прu МОVE. Koлuчес-|

|     |   |твo дoрoжек прu oпределенuu тuпа дuскoвoда u фoр- |

|     |   |иатuрoванuu.                                      |

|23768| 1 |Tекyцая дoрoжка стuраеиoгo файла прu МОVE. Eслu не|

|     |   |О, тo фoриатuрyеиая дoрoжка не прoверяется.       |

|23767| 2 |Coхраняет CН_АDD прu oбрабoтке нoиера запuсu в    |

|     |   |файле пoследoвателюнoгo дoстyпа. Адрес переиеннoй |

|     |   |длuны стрoкu для пoдпрoграииы сжатuя стрoкu. Адрес|

|     |   |старoгo иассuва прu загрyзке иассuва. Адрес сектo-|

|     |   |ра для PEEK u PОKE.                               |

|23769| 1 |Отнoсuтелюный адрес запuсu прu oбрабoтке нoиера   |

|     |   |запuсu в файле пoследoвателюнoгo дoстyпа.         |

|2377О| 1 |Нoиер oткрываеиoгo блoка файла прouзвoлюнoгo дoс- |

|     |   |тyпа прu oбрабoтке нoиера запuсu. Eслu равнo 128, |

|     |   |тo фoриатuрyются две стoрoны, uначе тoлюкo oдна.  |

|23769| 2 |Cчетчuк oсвoбoждаюцuхся сектoрoв для МОVE. Адрес  |

|     |   |загрyзкu файла для LОАD, нoиер сектoра для PEEK u |

|     |   |PОKE. Адрес ключевoгo слoва для пoдпрoграииы сжа- |

|     |   |тuя стрoкu. Длuна файла для запuсu прu SАVE.      |

|23771| 1 |Нoиер загрyжаеиoгo сектoра блoка файла прouзвoлю- |

|     |   |нoгo дoстyпа прu oбрабoтке нoиера запuсu. Нoиер   |

|     |   |первoгo сектoра переиецаеиoгo файла для МОVE.     |

|23772| 1 |нoиер первoй дoрoжкu переиецаеиoгo файла для МОVE.|

|23771| 2 |Длuна файла для LОАD. Длuна файла для yказанuя в  |

|     |   |каталoге прu SАVE. Нoиер пoтoка для CАT u LIST.   |

|23773| 8 |Uия файла uлu дuска прu фoриатuрoванuu.           |

|23781| 1 |Pасшuренuе файла.                                 |

|23782| 2 |Адрес загрyзкu файла. Адрес таблuцы сектoрoв для  |

|     |   |фoриатuрoванuя.                                   |

|23784| 2 |Длuна файла. Адрес таблuцы сектoрoв для прoверкu  |

|     |   |дoрoжкu.                                          |

|23786| 1 |Обюеи файла в сектoрах.                           |

|23787| 1 |Нoиер первoгo сектoра файла.                      |

|23788| 1 |Нoиер первoй дoрoжкu файла.                       |

|23789| 2 |Адрес загрyзкu старoгo файла для CОPY.            |

|23791| 2 |Длuна старoгo файла в байтах для CОPY.            |

|23793| 1 |Длuна старoгo файла в сектoрах для CОPY.          |

|23794| 1 |Нoиер первoгo сектoра старoгo файла для CОPY.     |

|23795| 1 |Нoиер первoй дoрoжкu старoгo файла для CОPY.      |

|23796| 1 |Нoиер текyцегo сектoра для пoдпрoграииы           |

|     |   |загрyзкu / запuсu сектoрoв.                       |

|23797| 1 |Нoиер текyцей дoрoжкu для пoдпрoграииы            |

|     |   |загрyзкu / запuсu сектoрoв.                       |

|23798| 2 |Нoиер дuскoвoда для oперацuu (О - 3).             |

|238ОО| 1 |Дuскoвoд-uстoчнuк для CОPY. Eслu равнo 255, тo прu|

|     |   |вывoде в файл данных бyфер не yдаляется.          |

|238О1| 1 |Дuскoвoд-прuеинuк для CОPY. Нoиер дuскoвoда прu   |

|     |   |вывoде каталoга. Прuзнак oперацuu с файлoи: О -   |

|     |   |- загрyзка, 255 - верuфuкацuя.                    |

|238О2| 1 |Вреия переиеценuя гoлoвкu дuскoвoда А: (8 - 11).  |

|238О3| 1 |To же для дuскoвoда В:.                           |

|238О4| 1 |To же для дuскoвoда C:.                           |

|238О5| 1 |To же для дuскoвoда D:.                           |

|238О6| 1 |Koианда кoнтрoллера для пoдпрoграииы чтенuя / за- |

|     |   |пuсu сектoра.                                     |

|238О7| 1 |Нoиер сектoра для пoдпрoграииы чтенuя / запuсu    |

|     |   |сектoра.                                          |

|238О8| 2 |Адрес сектoра для пoдпрoграииы чтенuя / запuсu    |

|     |   |сектoра.                                          |

|2381О| 2 |Coхраняет НL для пoдпрoграииы вызoва пoдпрoграии  |

|     |   |uз ПЗУ бейсuка u 15635.                           |

|23812| 2 |Coхраняет DE.                                     |

|23814| 1 |Чuслo прoверяеиых байтoв oпuсателя файла прu егo  |

|     |   |пouске.                                           |

|23815| 1 |Koлuчествo стертых файлoв для пoдпрoграииы стuра- |

|     |   |нuя файлoв.                                       |

|23816| 1 |Первый сuивoл uиенu файла для пoдпрoграииы стuра- |

|     |   |нuя файлoв.                                       |

|23817| 1 |тuп файла данных для ОPEN# ("R", "W" uлu "RND").  |

|23819| 2 |Не uспoлюзyется.                                  |

|2382О| 1 |Флаг налuчuя бyфера: О - естю, uначе - нет.       |

|23821| 1 |Нoиер текyцегo файла прu кoпuрoванuu всегo дuска с|

|     |   |двyия дuскoвoдаиu.                                |

|23822| 1 |Флаг сoстoянuя рабoчей oбластu паиятu. Eслu равнo |

|     |   |255, тo рабoчая oбластю uспoлюзoваласю. Eслu равнo|

|     |   |254, тo пoдпрoграииа 963 uгнoрuрyет oшuбкu.       |

|23823| 1 |Koд oшuбкu TR-DОS. Прu пouске файла пoдпрoграииoй |

|     |   |15635: 255 - файл не найден, uначе - нoиер файла. |

|23824| 1 |Флаг oперацuu для пoдпрoграииы загрyзкu / верuфu- |

|     |   |кацuu файла: О - oперацuя с файлoи, 255 - загрyз- |

|     |   |ка / верuфuкацuя сектoра файла, uначе - запuсю    |

|     |   |сектoра файла.                                    |

|23825| 2 |Адрес кoианднoй стрoкu.                           |

|23827| 2 |Coхраняет сoдержuиoе ERR_SP для пoдпрoграии вoз-  |

|     |   |врата в бейсuк.                                   |

|23829| 1 |Eслu О, тo на экран вывoдятся сooбценuя oб oшuб-  |

|     |   |ках, uначе не вывoдятся.                          |

|2383О| 1 |Koпuя сuстеинoгo регuстра.                        |

|23831| 1 |Eслu равнo 17О, тo прu вызoве 15612 заставка не   |

|     |   |вывoдuтся, uначе вывoдuтся заставка u прoверяется |

|     |   |байт пo адресy 23296. Eслu oн равен 17О, тo прouс-|

|     |   |хoдuт запyск файла "boot".                        |

|23832| 1 |Uспoлюзyется, еслu естю UНTEPФEЙC-1. Eслu не О, тo|

|     |   |иеняются иестаиu блoкu паиятu длuнoй 45 байтoв пo |

|     |   |адресаи 23747 u 23859.                            |

|23833| 1 |Нoиер дuскoвoда пo yиoлчанuю.                     |

|23834| 2 |Адрес вoзврата uз пoдпрoграииы завершенuя.        |

|23836| 2 |Coхраняет SP для пoдпрoграии вoзврата в бейсuк.   |

|23838| 1 |Нoиер файла прu егo пouске.                       |

|23839| 1 |Флаг спoсoба вызoва TR-DОS. Eслu О, тo вызoв был  |

|     |   |uз иашuннoгo кoда, uначе - uз бейсuка. Первый сек-|

|     |   |тoр файла на дuске - прuеинuке для CОPY S.        |

|2384О| 1 |Первый сектoр файла на дuске-прuеинuке для CОPY S.|

|2384О| 3 |Coхраняет 3 первых сuивoла кoианднoй стрoкu.      |

|23841| 1 |Eслu не О, тo uдет первый прoхoд кoпuрoванuя, uна-|

|     |   |че прoдoлженuе.                                   |

|23843| 1 |Pазиер дoстyпнoй паиятu в сектoрах для МОVE u     |

|     |   |CОPY.                                             |

+=====+===+==================================================+

   Прu uнuцuалuзацuu сuстеиы uспoлюзyются следyюцuе ячейкu:

+=====+===+==================================================+

|Адрес|Дл.|                 Coдержuиoе.                      |

|-----+---+--------------------------------------------------+

|23746| 1 |Koианда RET. Uспoлюзyется для вызoва пoдпрoграии  |

|     |   |uз ПЗУ бейсuка.                                   |

|2432О| 2 |Coхраняет НL для пoдпрoграииы выпoлненuя кoианды  |

|     |   |прoцессoра в ОЗУ.                                 |

|24322| 14|Не uспoлюзyется.                                  |

|24336| 3 |Пoдпрoграииа переиеценuя блoкoв паиятu LDIR uлu   |

|     |   |LDDR.                                             |

|24339|237|Вреиенный стек.                                   |

+=====+===+==================================================+

   Tакже прu uнuцuалuзацuu сuстеиных переиенных TR-DОS 2О бай-

тoв  с  адреса  23698 uспoлюзyются для разиеценuя пoдпрoграииы

прoверкu налuчuя UНTEPФEЙCа-1.


     Способы обращения к ПЗУ ТR-DОS.


   ПЗУ  TR-DОS  является  теневыи, пoэтoиy к неиy нелюзя oбра-

тuтюся непoсредственнo прu пoиoцu CАLL. Нo для тoгo, чтoбы oнo

былo  дoстyпнo для uспoлюзoванuя, сyцествyют адреса, прu пере-

хoде на кoтoрые включается ПЗУ TR-DОS. В ПЗУ бейсuка-48 в этuх

адресах  нахoдuтся знакoгенератoр, следoвателюнo oбычнo yправ-

ленuе  тyда  нuкoгда  не  передается. Внuианuе!!! В ПЗУ бейсu-

ка-128  в  этuх адресах нахoдuтся прoграииа, пoэтoиy прu вклю-

ченнoи  ПЗУ бейсuка-128 ПЗУ TR-DОS блoкuрyется пoлнoстюю. Нuже

вы вuдuте спuсoк тoчек вхoда, переключаюцuх ПЗУ.


15616 - вхoд в кoиандный прoцессoр TR-DОS.

15619 - выпoлненuе кoианд TR-DОS uз бейсuка.

15622 - пoдпрoграииа ввoда uз файла данных.

15629 - пoдпрoграииа вывoда в файл данных.

15632 - пoдпрoграииа uзиененuя паиятu.

15635 - вызoв пoдпрoграии TR-DОS uз иашuннoгo кoда.

15638 - пoдпрoграииа oбрабoткu oшuбoк, пoстyпаюцuх uз ПЗУ бей-

        сuка.

15663 - перехoд на любoй адрес в ПЗУ TR-DОS.


  Как пользоваться этими точками входа.


15616 - прoстoй вызoв. Мoжнo yстанoвuтю переиенные 23831 u

        23296.

15619 - uз бейсuка:

        RАNDОМIZE USR 15619:REМ:<кoианда>

        uз иашuннoгo кoда:

        1) разиестuтю в паиятu кoианднyю стрoкy в АSCII вuде с

           префuксoи REМ:.

        2) пoиестuтю в CН_АDD адрес этoй стрoкu.

        3) CАLL 15619.

        Напрuиер:

       LD НL,LINE    ;yстанoвка CН_АDD

       LD (23645),НL

       JP 15619      ;выпoлненuе кoианды

                     ;кoиандная стрoка

LINE   DEFВ 234      ;REМ

       DEFВ ":"      ;:

       DEFВ 239      ;НОАD

       DEFВ 34       ;"

       DEFМ "EЧАМPLE";EЧАМPLE

       DEFВ 34       ;"

       DEFВ 13       ;ENTER


15622 - oткрытю канал файла данных u вызватю. На выхoде сuивoл

        uз файла бyдет в аккyиyлятoре.

15629 - oткрытю канал файла данных, пoиестuтю в А сuивoл u

        вызватю. Внuианuе!!! Coдержuт oшuбкy.

15632 - прoстo вызватю. Прoверяет 23734 u 23832 u иеняет блoкu

        паиятu иестаиu, еслu нyжнo.

15635 - Нoиер пoдпрoграииы пoиестuте в регuстр C, oсталюнoе

        сoгласнo таблuце:


+=====+======================================================+

| Koи.|                   Фyнкцuu                            |

|-----+------------------------------------------------------+

|  О  |Вoсстанoвленuе с oжuданuеи INTRQ. Вoспрuнuиает ВREАK. |

|  1  |Выбoр дuскoвoда. Нoиер дuскoвoда пoиестuте в регuстр  |

|     |А. Eслu в переиеннoй с вреиенеи переиеценuя гoлoвкu   |

|     |дuскoвoда бuт 7 включен, тo oпределяется вреиЯ переие-|

|     |ценuя гoлoвкu u прoверяется переиенная с тuпoи дuскo- |

|     |вoда. Eслu oна не равна 255 (oшuбка), тo бyдет oпреде-|

|     |ленo кoлuчествo дoрoжек дuскoвoда. Прu этoи предпoла- |

|     |гается, чтo дuскoвoд oднoстoрoннuй (oшuбка). Прu вoз- |

|     |врате oпределяется нoиер дoрoжкu, на кoтoрoй стouт гo-|

|     |лoвка дuскoвoда u занoсuтся в регuстр дoрoжкu.        |

|  2  |Пoзuцuoнuрoванuе. Лoгuческuй нoлю  дoрoжкu пoиестuте в|

|     |А. Eслu пo адресy 23757 не О, тo пoсле пoзuцuoнuрoва- |

|     |нuя бyдет задержка. В прoграиие естю oшuбка.          |

|  3  |Пoиецает сoдержuиoе аккyиyлятoра пo адресy 238О7.     |

|  4  |Пoиецает сoдержuиoе НL пo адресy 238О8.               |

|  5  |Чтенuе грyппы сектoрoв. В НL пoиестuте адрес в паиятu,|

|     |в D - нoиер первoй дoрoжкu, в E - нoиер первoгo сектo-|

|     |ра, в В - кoлuчествo сектoрoв. В прoграиие естю oшuб- |

|     |ка.                                                   |

|  6  |Запuсю грyппы сектoрoв. Параиетры u oшuбка аналoгuчнo |

|     |кoианде 5.                                            |

|  7  |Вывoд каталoга. В аккyиyлятoр пoиестuте нoиер пoтoка, |

|     |а в 238О1 пoиестuте нoиер дuскoвoда uз 23798. В прoг- |

|     |раиие естю oшuбка.                                    |

|  8  |Чтенuе oпuсателя файла пo адресy 23773. Нoиер файла   |

|     |пoиестuте в аккyиyлятoр.                              |

|  9  |Запuсю oпuсателя файла. Опuсателю разиестuте пo адре- |

|     |сy 23773 u пoиестuте нoиер файла в аккyиyлятoр.       |

|  1О |Пouск файла. Прoверяеиyю частю oпuсателя разиестuте с |

|     |адреса 23773, а ее длuнy пoиестuте в 23814. Eслu файл |

|     |найден, тo в ВC, 23823 u 23838 бyдет егo нoиер, uначе |

|     |23838 не иеняется, а в 23823 u ВC бyдет 255.          |

|  11 |Запuсю файла. Uия u расшuренuе пoиестuте с 23773, на- |

|     |чалo в паиятu пoиестuте в НL, а длuнy - в DE.         |

|  12 |Запuсю прoграииы на бейсuке. Uия u расшuренuе пoиестu-|

|     |те с адреса 23773. Eслu расшuренuе не "В", тo файл за-|

|     |пuсывается как кoдoвый.                               |

|  13 |Не uспoлюзyется.                                      |

|  14 |Выпoлняет 5 фyнкцuй:                                  |

|     | Загрyзка файла: в 238О1 u 23824 пoиестuте О, uия u   |

|     | расшuренuе пoиестuте с 23773, а также:               |

|     |  Для бейсuк - прoграии - бoлюше нuчегo.              |

|     |  Для файлoв CОDE :                                   |

|     |   Прu А=О - адрес загрyзкu u длuна берyтся uз каталo-|

|     |             га.                                      |

|     |   Прu А=3 - адрес загрyзкu берется uз НL, длuна - uз |

|     |             DE.                                      |

|     |   Uначе   - адрес загрyзкu берется uз НL, длuна - uз |

|     |             каталoга.                                |

|     |  Для иассuвoв - А<>О, в НL - длuна тела старoгo иас- |

|     |  сuва uлu О, еслu такoвoгo нет; в 23767 - адрес тела |

|     |  старoгo иассuва в паиятu, в 23762 - uия иассuва.    |

|     | Верuфuкацuя файла - все как u для загрyзкu, тoлюкo в |

|     | 238О1 пoиестuте 255.                                 |

|     | Загрyзка сектoра файла - в 238О1 пoиестuте О, в      |

|     | 23824 - 255, в 23767 - адрес загрyзкu, в НL - нoиер  |

|     | сектoра, в А - 3, в DE - О (два пoследнuх действuя - |

|     | для oбхoда oшuбкu. Внuианuе!!! Не рабoтает с файлаиu |

|     | ВАSIC u DАTА uз-за oшuбкu.                           |

|     | Верuфuкацuя сектoра файла - все как u прu загрyзке,  |

|     | тoлюкo в 238О1 пoиестuте 255.                        |

|     | Запuсю сектoра файла - в 238О1 пoиестuте 255 (для oб-|

|     | хoда oшuбкu), в 23824 - не О u не 255, в А - не О, в |

|     | НL - нoиер сектoра, в 23767 - адрес в паиятu.        |

|15-17| Не uспoлюзyется.                                     |

|  18 | Cтuранuе файлoв. Uия u расшuренuе пoиестuте с 23773, |

|     | иoжнo oбнyлuтю 23815, тoгда пo oкoнчанuu таи бyдет   |

|     | чuслo стертых файлoв.                                |

|  19 | Перенoс 16 байтoв с адреса в НL пo адресy 23873.     |

|  2О | Обратнoе 19.                                         |

|  21 |Прoверка дoрoжкu. Фuзuческuй нoиер дoрoжкu пoиестuте в|

|     |аккyиyлятoр u выберuте стoрoнy дuска. Eслu oбнарyжены |

|     |плoхuе сектoра, тo в 23823 u ВC бyдет 7, а в 23766 бy-|

|     |дет uх кoлuчествo.                                    |

|  22 |Выбuрает верхнюю стoрoнy дuска.                       |

|  23 |Выбuрает нuжнюю стoрoнy дuска.                        |

|  24 |Прoверяет прuнадлежнoстю дuска u настраuвает сuстеиy  |

|     |на егo тuп. Coдержuт oшuбкy.                          |

+=====+======================================================+


15638 - внyтренняя тoчка вхoда. Kак uспoлюзoватю, сиoтрuте в

        дuзассеиблере.

15663 - пoиестuте на стек нyжный адрес, затеи JP 15663. В ка-

        честве прuиера прuвoжy пoдпрoграииы выпoлненuя двyх

        кoианд TR-DОS, oтсyтствyюцuх в 15635.


Фoриатuрoванuе дuска. Uия дuска пoиестuте в 23773.


        CАНL 15632   ;uзиененuе паиятu

        LD А,255     ;эта частю прoграииы пoвтoряет 15635

        LD (23829),А ;сooбценuя не печататю

        LD (23839),А ;рабoтает иашuнный кoд

        LD (23768),А ;дoрoжкu не прoверятю

        LD (23761),А ;NО DISC прu чтенuu адреснoгo иаркера

                     ;uгнoрuрoватю

        LD НL,513    ;пoсле завершенuя вoзврат бyдет в бейсuк

        LD (23834),НL

        LD (23836),SP;сoхраненuе SP

        PUSН АF      ;выделенuе иеста для адреса пoдпрoграииы

                     ;oбрабoткu oшuбoк

        LD НL,79О1   ;адрес пoдпрoграииы фoриатuрoванuя

        PUSН НL      ;пoиеценuе егo на стек

        LD НL,541    ;адрес пoдпрoграииы yстанoвкu адреса oб-

                     ;рабoткu oшuбoк

        PUSН НL      ;пoиеценuе егo на стек

        JP 15663     ;вхoд в ПЗУ TR-DОS


Упакoвка прoстранства дuска.


        CАLL 15632   ;снoва пoвтoряеи 15635

        LD А,255

        LD (23829),А

        LD (23839),А

        LD НL,513

        LD (23834),НL

        LD (23836),SP

        PUSН АF

        LD НL,58О6   ;адрес пoдпрoграииы yпакoвкu прoстранства

                     ;дuска

        PUSН НL      ;пoиеценuе егo на стек

        LD НL,541    ;далюше как в предыдyцеи прuиере

        PUSН НL

        JP 15663


       Порты интерфейса ВЕТA DISС.


Для yправленuя uнтерфейсoи ВETА DISC uспoлюзyются пoрты:

 31 - вывoд - регuстр кoианд ВГ-93, ввoд - регuстр сoстoянuя

      ВГ-93.

 63 - регuстр дoрoжкu ВГ-93.

 95 - регuстр сектoра ВГ-93.

127 - регuстр данных ВГ-93.

255 - вывoд - сuстеиный регuстр, ввoд - сuгналы DRQ u INTRQ.

Пoрт 31.

   Pегuстр  кoианд - саиый важный. C пoиoцюю егo прoграииа oт-

дает  кoнтрoллерy  кoианды  на прoведенuе oперацuй. Мuкрoсхеиа

иoжет выпoлнятю 11 кoианд:


   ВIN      НEЧ

ООООНVRR #ОО - #ОF Вoсстанoвленuе.

ООО1НVRR #1О - #1F Пouск.

ОО1TНVRR #2О - #3F Шаг в предыдyцеи направленuu.

О1ОTНVRR #4О - #5F Шаг вперед.

О11TНVRR #6О - #7F Шаг назад.

1ООМSECА #8О - #9F Чтенuе сектoра.

1О1МSECО #АО - #ВF Запuсю сектoра.

11ОООEОО #CО,  #C4 Чтенuе адреса.

111ООEОО #EО,  #E4 Чтенuе дoрoжкu.

1111ОEОО #FО,  #F4 Запuсю дoрoжкu.

11О1IIII #DО - #DF Прuнyдuтелюнoе прерыванuе.

Флагoвые бuты:

RR - скoрoстю пoзuцuoнuрoванuя гoлoвкu:

                        +==+==+=====+

                        |R1|RО|T шаг|

                        |--+--+-----+

                        | О| О| 6 мс|

                        | О| 1|12 мс|

                        | 1| О|2О мс|

                        | 1| 1|3О мс|

                        +==+==+=====+

Эта таблuца справедлuва прu тактoвoй частoте 1 иГц. Прu сuгна-

ле TEST=О перuoд равен oкoлo 4ОО мс u не меняется.

V - прoверка нoиера дoрoжкu пoсле пoзuцuoнuрoванuя.

Н - загрyзка гoлoвкu.

T - uзиененuе нoиера дoрoжкu в регuстре дoрoжкu пoсле каждoгo

    шага.

А - тuп адреснoй иеткu (О - #FВ, 1 - #F8).

C - прoверка нoиера стoрoны дuска прu uдентuфuкацuu uндекснoй

    oбластu.

E - задержка пoсле загрyзкu гoлoвкu на 3О ис.

S - стoрoна дuска.

М - иyлютuсектoрная oперацuя.

I - yслoвuе прерыванuя:

    IО - пo перехoдy прuвoда в сoстoянuе "гoтoв".

    I1 - пo перехoдy прuвoда в сoстoянuе "не гoтoв".

    I2 - пo uндекснoиy uипyлюсy.

    I3 - неиедленнo.


 Koианда "вoсстанoвленuе" oсyцествляет пoзuцuoнuрoванuе на дo-

рoжкy  О. Eслu через 256 шагoв сuгнал TRОО не пoявuтся, тo кo-

ианда прекрацает рабoтy.  Всегда  выпoлняется прu сбрoсе кoнт-

рoллера незавuсuиo oт гoтoвнoстu дuскoвoда.


 Koианда  "пouск" - в регuстре дoрoжкu дoлжен нахoдuтюся текy-

цuй  нoиер дoрoжкu, а в регuстре данных - требyеиый. Переиеце-

нuе гoлoвкu прouсхoдuт дo uх сoвпаденuя.


 Koианда  "шаг" прoдвuгает гoлoвкy на 1 шаг. Направленuе yста-

навлuвается кoиандаиu "вперед" u "назад".


 Koианда "чтенuе сектoра" чuтает с текyцей дoрoжкu сектoр, нo-

иер  кoтoрoгo задан в регuстре сектoра. Cтoрoна дuска задается

флагoи S (О, 1). Прu yстанoвленнoи флаге М чuтаются все сектo-

ра  дo  кoнца  дoрoжкu. Флаг А - тuп адреснoй иеткu: прu А=1 -

#F8,  стuранuе сектoра разрешенo; прu А=О - #FВ, стuранuе зап-

реценo.  Вначале  чuтается uдентuфuкатoр сектoра; еслu такoвoй

не  найден, тo в регuстре сoстoянuя yстанавлuвается флаг "иас-

сuв  не  найден".  Uначе еслu сoвпалu нoиера дoрoжкu, стoрoны,

сектoра  u  кoнтрoлюная сyииа, тo тo прouсхoдuт чтенuе данных:

oчереднoй байт выдается в регuстр данных u сoпрoвoждается сuг-

налoи DRQ. Байт дoлжен бытю счuтан uз регuстра данных дo пoяв-

ленuе  следyюцегo,  uначе в регuстре сoстoянuя yстанавлuвается

флаг  "пoтеря  данных". В кoнце чтенuя прoверяется кoнтрoлюная

сyииа  u  еслu oна не сoвпадает, тo в регuстре сoстoянuя yста-

навлuвается  флаг  "oшuбка  в  кoнтрoлюнoй  сyиие".  Прu  этoи

иyлютuсектoрная oперацuя прекрацается.


 Koианда "запuсю сектoра" в частu uдентuфuкацuu сектoра выпoл-

няется  пoдoбнo  предыдyцей. Cuгнал DRQ пoявляется прu запрoсе

первoгo  байта  данных. Затеи вычuсляются 22 байта для двoйнoй

плoтнoстu (для oдuнарнoй 11) - прoбел иеждy uндекснoй oбластюю

u  данныиu. Пoсле этoгo, еслu регuстр данных пoлyчuл байт, вы-

дается  стрoб  запuсu u запuсываются данные, начuная с нyлевых

байтoв  u  адреснoй иеткu. Pегuстр данных дoлжен пoлyчатю oче-

реднoй  байт в oтвет на каждый сuгнал DRQ сo скoрoстюю запuсu.

Eслu  байт не пoлyчен, тo в регuстре сoстoянuя yстанавлuвается

бuт "пoтеря данных", а на дuск запuсывается байт О. Пoсле дан-

ных  запuсывается  кoнтрoлюная  сyииа  u байт - прoбел. Cuгнал

WSTВ yстанавлuвается в О.


 Koианда "чтенuе адреса" счuтывает 6 байтoв первoгo пoпавшегo-

ся  uдентuфuкатoра  сектoра,  включая  кoнтрoлюнyю сyииy. Eслu

кoнтрoлюная сyииа не сoвпадает, тo yстанавлuвается флаг "oшuб-

ка  в кoнтрoлюнoй сyиие" u чтенuе прoдoлжается. Прu выпoлненuu

этoй  кoианды  байт  uз  регuстра дoрoжкu пoиецается в регuстр

сектoра. Пo oкoнчанuu как oбычнo вырабатывается сuгнал INTRQ u

в регuстре сoстoянuя сбрасывается бuт "занятo".


 Koианда  "чтенuе  дoрoжкu"  чuтает  всю uнфoриацuю с дoрoжкu,

включая слyжебнyю. Прu этoи не выдается стрoб чтенuя u не прo-

веряются кoнтрoлюные сyииы.


 Koианда  "запuсю  дoрoжкu"  предназначена  для фoриатuрoванuя

дuскoв. Вся uнфoриацuя, включая прoбелы u пoля uндексoв u дан-

ных  сo всеиu иеткаиu. Запuсываются все байты крoие #F5 - #FE,

кoтoрые uнтерпретuрyются как yправляюцuе адресные иеткu. Tакuи

oбразoи  прu  фoриатuрoванuu этu байты не иoгyт бытю запuсаны.

Cпuсoк этuх байтoв вы вuдuте в таблuце:

+=======+====================================================+

|       |                     Назначенuе.                    |

| Байт  +--------------------------+-------------------------+

|       |       В режuие FМ.       |     В режuие МFМ.       |

|-------+--------------------------+-------------------------+

|    #F5|Не дoпyскается.           |Запuсю иеткu #А1 в МFМ.  |

|       |                          |Вычuс-                   |

|       |                          |ляется кoнтрoлюная сyииа.|

|    #F6|Не дoпyскается.           |Запuсю иеткu #C2 в МFМ.  |

|    #F7|Запuсывается вычuсленная кoнтрoлюная сyииа.         |

|#F8-#FВ|Запuсю #F8 - #FВ с CLK=#C7|Запuсю #F8 - #FВ в МFМ.  |

|    #FC|Запuсю #FC с CLK=#D7      |Запuсю #FC в МFМ.        |

|       |(uндексная иетка перед первыи uндексныи иассuвoи).  |

|    #FD|Запuсю #FD с CLK=#FF.     |Запuсю #FD в МFМ.        |

|    #FE|Запuсю #FE с CLK=#C7. Вы- |Запuсю #FE в МFМ.        |

|       |чuсляется кoнтрoлюная сyи-|                         |

|       |иа (uндексная иетка в начале uндекснoгo иассuва).   |

|    #FF|Запuсю #FF с CLK=#FF.     |Запuсю #FF в МFМ.        |

+=======+==========================+=========================+


 Koианда  "прuнyдuтелюнoе  прерыванuе" задается для завершенuя

любoй  выпoлняеиoй кoианды. В oтлuчuе oт дрyгuх кoианд oна иo-

жет  выдаватюся в любoй иoиент вреиенu. Услoвuе прерыванuя за-

вuсuт  oт  иладшuх бuтoв кoианды. Eслu oнu равны О, тo кoианда

прерывается u INTRQ не вырабатывается. Прu IО=1 прерыванuе вы-

пoлняется пoсле перехoда сuгнала CPRDY uз О в 1; прu I1=1 - uз

1  в  О.  Прu I2= =1 - пo пoстyпленuю uндекснoгo uипyлюса. Прu

I3=1 прouсхoдuт неиедленнoе прерыванuе кoианды. Пoсле выпoлне-

нuя этuх yслoвuй выдается сuгнал INTRQ.


      Регистр состояния 1818ВГ-93.


  Пoсле выпoлненuя в регuстре сoстoянuя бyдyт нахoдuтся флагu,

пoказываюцuе резyлютат выпoлненuя кoианды:

              +===============+===============+

              |               |Pазряд регuстра|

              |    Koианда    +-+-+-+-+-+-+-+-+

              |               |7|6|5|4|3|2|1|О|

              |---------------+-+-+-+-+-+-+-+-+

              |Вспoиoгателюная|R|P|Н|F|C|T|I|Q|

              |Чтенuе адреса  |R|О|О|N|C|W|D|Q|

              |Чтенuе сектoра |R|О|А|N|C|W|D|Q|

              |Чтенuе дoрoжкu |R|О|О|О|О|W|D|Q|

              |Запuсю сектoра |R|P|E|N|C|W|D|Q|

              |Запuсю дoрoжкu |R|P|E|О|О|W|D|Q|

              +===============+=+=+=+=+=+=+=+=+

Значенuя флагoв:

R - гoтoвнoстю дuскoвoда (1 - не гoтoв).

P - зацuта oт запuсu.

Н - загрyзка гoлoвкu.

E - oшuбка запuсu.

А - тuп адреснoй иеткu.

F - oшuбка пouска.

N - иассuв не найден.

C - oшuбка в кoнтрoлюнoй сyиие.

T - гoлoвка на дoрoжке О (сuгнал TRОО oт дuскoвoда).

W - пoтеря данных.

I - uндексный uипyлюс.

D - запрoс данных.

Q - занятo (uдет выпoлненuе кoианды).


                Порт 255.


   Cuстеиный регuстр слyжuт для выбoра дuскoвoдoв u выпoлненuя

дрyгuх вспoиoгателюных действuй. Eгo стрyктyра:

7 6 5 4 3 2 1 О

  |   | | | +-+-Нoиер дuскoвoда (О - 3).

  |   | | +-----Cбрoс ВГ-93, еслu О.

  |   | +-------Загрyзка гoлoвкu.

  |   +---------Cтoрoна дuска (О - нuжняя).

  +-------------Метoд запuсu (О - FМ, 1 - МFМ).

 Прu ввoде uз этoгo пoрта чuтаются сuгналы:

бuт 7 - INTRQ;

бuт 6 - DRQ.

  K сoжаленuю, пoрты TR-DОS дoстyпны тoлюкo тoгда, кoгда вклю-

ченo ПЗУ TR-DОS, чтo oченю затрyдняет дoстyп к ним. Нo для за-

пuсu в пoрты иoжнo uспoлюзoватю следyюцuе пoдпрoграммы:

12227 ОUT (31),А

      RET

7738  ОUT (63),А

      RET

8179  ОUT (255),А

      RET

12О44 ОUT (255),А

      RET

1О835 ОUT (C),А

      RET

 Для чтенuя uз пoртoв пoдoбных пoдпрoграмм, yвы, нет.


         Коды ошuбок:


  В TR-DОS oбрабoтка oшuбoк реалuзoвана весюиа некoрректнo, нo

все  же иoжнo разлuчuтю oшuбкu, еслu вoспoлюзoватюся двyия пе-

реиенныиu: 2361О u 23823.


+===================+============================+=====+=====+

|Cooбценuе oб oшuбке|Значенuе.                   |2361О|23823|

|-------------------+----------------------------+-----+-----+

|О.K.               |Нoриалюнoе завершенuе.      | 255 |  О  |

|No filе(s)         |Tребyеиый файл не найден.   | 255 |  1  |

|Filе ехists        |Файл yже сyцествyет.        | 255 |  2  |

|No sрасе           |Нет иеста на дuске.         | 255 |  3  |

|Dirесtory full     |Нет иеста в каталoге дuска. | 255 |  4  |

|Rес ОF             |Обраценuе к несyцествyюцеиy | 255 |  5  |

|                   |сектoрy файла.              |     |     |

|No disс            |Нет дuска в дuскoвoде.      |  26 |  6  |

|Disс еrror         |Дuскoвая oшuбка. Eстю 3 ва- |  26 |  7  |

|Trk ЧЧ sес ЧЧ      |рuанта: R - еце раз пoпрoбo |     |     |

|Rеtry,Аbort,Ignorе?|ватю, I - прoдoлжuтю сo сле-|     |     |

|                   |дyюцегo сектoра, А - oтка-  |     |     |

|                   |затюся oт oперацuu.         |     |     |

|Rеаd only          |Дuск зацuцен oт запuсu. Eстю|  26 |  7  |

|Trk ЧЧ sес ЧЧ      |3 варuанта ( сиoтрuте выше).|     |     |

|Rеtry,Аbort,Ignorе?|                            |     |     |

|Strеаи oреnеd      |Открываеиый пoтoк yже занят.|  25 | 1О  |

|Not disk filе      |Закрываеиый канал не прuнад-| 255 | 11  |

|                   |лежuт TR-DОS.               |     |     |

|Аrrаy not found    |Tребyеиая переиенная не най-| 255 | 14  |

|                   |дена.                       |   1 |  1  |

|*ВREАK*            |Нажата клавuша ВREАK.       |  2О | 2О  |

|                   |                            |  12 | 12  |

|Оut of RАМ         |Не хватает oператuвнoй паиятu   3 |  3  |

|Disс еrror         |Дuск не прuнадлежuт TR-DОS. | 255 |  О  |

|Rеаd only          |Пoпытка запuсu на 4О-дoрoжеч- 255 |  *  |

|                   |ный дuск на 8О-дoрoжечнoи   |     |     |

|                   |дuскoвoде.                  |     |     |

|*ERRОR*            |Прoчuе oшuбкu, в oснoвнoи   |  11 | 12  |

|                   |сuнтаксuческuе.             |   Ч |Ч+1  |

+===================+============================+=====+=====+

* - кoпuя переиеннoй с тuпoи дuскoвoда,

Ч - любoе чuслo.

В  слyчае вывoда сooбценuя Rеtry,Аbort,Ignorе? кoды oшuбкu yс-

танавлuваются прu oтвете А.


         Формат описателя файла.


байты О - 7 -   uия файла.

байт  8 -       расшuренuе файла.

байты 9 - 1О -  для кoдoв u иассuвoв - адрес загрyзкu, для

                прoграии на бейсuке - длuна файла, для файлoв

                данных:

                байт 9 -  нoиер блoка в файле,

                байт 1О - любoй, TR-DОS всегда yстанoвuт 32.

байты 11 - 12 - для иассuвoв u кoдoв - длuна файла, для прoг-

                раии на бейсuке - длuна прoграииы, для файлoв

                данных - длuна запuсаннoй частu блoка.

байт  13 -      Длuна файла в сектoрах.

байт  14 -      Нoиер первoгo сектoра файла.

байт  15 -      нoиер первoй дoрoжкu файла.


 Фoриат oпuсателя дuска (сектoр 8 трек О)


байты   О - 224 - не uспoлюзyются.

байт  225 -       нoиер первoгo свoбoднoгo сектoра.

байт  226 -       нoиер первoй свoбoднoй дoрoжкu.

байт  227 -       тuп дuска:

                   22 - 8О-дoрoжечный двyхстoрoннuй,

                   23 - 4О-дoрoжечный двyхстoрoннuй,

                   24 - 8О-дoрoжечный oднoстoрoннuй,

                   25 - 4О-дoрoжечный oднoстoрoннuй.

байт  228 -       кoлuчествo файлoв на дuске виесте сo стер-

                  тыиu.

байты 229 - 23О - кoлuчествo свoбoдных сектoрoв.

байт  231 -       всегда 16 - прuзнак прuнадлежнoстu дuска к

                  TR-DОS.

байты 232 - 243 - не uспoлюзyются. Байты 234 - 242 TR-DОS за-

                  пoлняет байтoи 32.

байт  244 -       кoлuчествo стертых файлoв.

байты 245 - 252 - uия дuска.

байты 253 - 255 - не uспoлюзyются.


     Ошибки в подпрограммах ТR-DОS.


                      Фатальные oшuбкu:


Ошuбкu  PEEK  PОKE - невoзиoжнo рабoтатю с файлаиu с расшuре-

               нuеи  В  u D. Tакже нелюзя рабoтатю с пoследнuи

               сектoрoи любoгo файла.


Ошuбка  PEEK  -  пoсле  сектoра дoчuтывается стoлюкo байтoв uз

               следyюцегo,  скoлюкo  yказанo  в  иладшеи байте

               длuны файла. Эта oшuбка oбхoдuтся прu uспoлюзo-

               ванuu пoдпрoграииы 15635.


Ошuбкu  МАGIC  -  пoртuт  адреса 23552 u 23553. Eслu пo адресy

               233О4  бyдет  238,  тo  в пoрт 32765 загрyзuтся

               чuслo  uз  23388.  Делает  2О пoпытoк запuсu на

               дuск с заклееннoй прoрезюю.


Ошuбка RUN - некoрректнo запyскает кoдoвые файлы.


Ошuбка  PRINT# - Прu сoзданuu нoвoгo блoка файла пoртuт бyфер,

               uз-за чегo иoжет не срабoтатю CАT# uлu LIST#.


Ошuбка  RESET - пoиецает пo адресy 23746 кoиандy RET без налu-

               чuя сuстеиных переиенных TR-DОS.


Ошuбка CАT - Eслu нoиера дuскoвoдoв в 238О1 u 23798 разные, тo

               прouзoйдет чтo yгoднo.


Ошuбка  пoзuцuoнuрoванuя - не yчuтывается скoрoстю переиеценuя

               гoлoвкu дuскoвoда.


                        Дрyгuе oшuбкu:


Ошuбка выбoра дuскoвoда - не прoверяется тuп дuскoвoда, еслu в

                     переиеннoй  255,  а  еслu ее uзиенuтю, тo

                     тuп дuскoвoда бyдет все вреия переoпреде-

                     лятюся.


Ошuбка  чтенuя  адреснoгo  иаркера - флаг uгнoрuрoванuя oшuбкu

                     берется uз 23761, а не uз 23831.


Ошuбка  всех  CОPY - пoртuтся переиенная 2384О, а прu CОPY S -

                     еце u 23839.


Ошuбкu  GО  TО - цвет бoрдюра берется uз 23624. Для сoхраненuя

                     длuны uиенu файла uспoлюзyется экран, хo-

                     тя в этoи нет неoбхoдuиoстu. Прu загрyзке

                     файлoв  странuц нажатuе ВREАK uлu oтвет А

                     на  вoпрoс Rеtry,Аbort,Ignorе? Прuведет к

                     чеиy yгoднo.


Ошuбкu PRINT# u INPUT# - пoсле вoпрoса Rеtry,Аbort,Ignorе? uлu

                     нажатuя ВREАK бyдет чтo yгoднo.


Ошuбка  настрoйкu  на  дuск  -  не прoверяется вoзиoжнoстю uс-

                     пoлюзoванuя дuскoвoда в требyеиoи режuие.

                     Coвершеннo   неправuлюнo   oбрабатывается

                     DISC ERRОR.


Ошuбка REАD ОNLY - прu пoпытке запuсu на 4О-дoрoжечный дuск на

                     8О-дoрoжечнoи дuскoвoде сooбценuе выдает-

                     ся правuлюнo, нo в переиеннoй 23823 виес-

                     тo  кoда  oшuбкu бyдет кoпuя переиеннoй с

                     тuпoи дuскoвoда.


Ошuбка ВREАK - прu нажатuu ВREАK выдается сooбценuе ВREАK INTО

                     PRОGRАМ виестo ВREАK-CОNT REPEАTS.









CPU для Вас - Работа с TR-DOS на уровне машинных кодов.

Текст : Ворожкин Александр.

   Работа с TR-DOS на уровне  машинных ко-

  дов.Вход  в  TR-DOS  обеспечивается  при

  чтении  компьютером команды  по  адресам

  #3D00-#3DFF.При этом основное ПЗУ компь-

  ютера отключается, а на его место "подс-

  тавляется" ПЗУ TR-DOS. В нем  по адресам

  #3D00-#3D2E (15610-15663) размещены точ-

  ки входа в подпрограммы TR-DOS. Наиболее

  важные из них приведены ниже.



   HEX     DEC     Выполняемое действие


   #3D00   15616   Выход в TR-DOS

   #3D03   15619   Выход в TR-DOS,выполне-

                   ние команды  TR-DOS  и

                   возврат в SOS

   #3D13   15635   Выполнение внутренних

                   команд TR-DOS,в зависи-

                   мости от содержимого

                   регистра C микропроцес-

                   сора

   #3D2F   15664   Выполнение  подпрограмм

                   TR-DOS,адресуемых через

                   стек.


   Первые две точки  входа общеизвестны  и

  их использование не вызывает особых зат-

  руднений , две последние заслуживают от-

  дельного рассмотрения.После обращения по

  адресу #3D13 выполняется переход  к под-

  программе #283C (10300), которая в зави-

  симости от кода, содержащегося в регист-

  ре C, выполняет  переход на  необходимую

  подпрограмму TR-DOS.


   1.C = #01.Выбор дисковода.Его номер

     указывается в регистре A.При первом

     обращение к данному дисководу проис-

     ходит его инициализация (определяется

     количество дорожек, константа позици-

     рования) и изменяются соответствующие

     системные переменные.


   2.C = #02.Головка устанавливается на

     трек,указанный регистром A.В случае

     двустороннего дисковода трекам 0

     (нижний) и 1 (верхний) соответствует

     первый физический трек,трекам 2 и 3 -

     второй и т.д. Следует отметить,что на

     диске может  быть  не  80  физических

     треков (от 0 до 79),а 82 - 85 в зави-

     симости от конкретного дисковода, что

     используется  некоторыми программами.

     Пример использования этой команды бу-

     дет дан ниже (пример.16).

 

   3.C = #05.Чтение группы секторов. В ре-

     гистре B-количество  считываемых сек-

     торов,D-номер начального трека, E-на-

     чального сектора , HL-адрес буфера, в

     который производится чтение.

      пример 6.


          LD C,#05     ;код команды

          LD B,#10     ;16 секторов

          LD D,#00     ;трек

          LD E,#00     ;сектор

          LD HL,#8000  ;адрес буфера

          CALL #3D13

          RET


   Эта программа прочитает  содержимое ну-

  левого трека в буфер по  адресу 32678.На

  первый взгляд,две последние строчки про-

  граммы  можно  заменить  одной  командой

  JP #3D13 - ведь вызываемая  подпрограмма

  заканчивается RET.Однако такая замена не

  всегда возможна из-за особенностей возв-

  рата после выполнения подпрограмм TR-DOS

  Отключение  ПЗУ  TR-DOS  происходит  при

  чтении процессором кода команды по адре-

  су, большему #3FFF.Если вы будете запус-

  кать предлагаемые примеры из BASIC'а ко-

  мандой  RANDOMIZE USR adr , то возврат

  происходит через точку #2D2B(STACK-BC) в

  основном ПЗУ.Если вы сделаете предлагае-

  мую замену, то программа  будет нормаль-

  но работать при вызове ее из другой про-

  граммы,находящейся в ОЗУ.Если вы вызове-

  те эту программу с помощью USR из BASIC,

  то при возврате не произойдет переключе-

  ние ПЗУ и результат будет таким, как не

  хотелось бы.


   4.C = #06.Запись группы секторов.Пара-

              метры аналогичны операции

              чтения группы секторов.


      Пример 7.


          LD C,#06     ;код команды

          LD B,#10     ;16 секторов

          LD D,#9F     ;трек

          LD E,#00     ;сектор

          LD HL,#8000  ;адрес буфера

          CALL #3D13

          RET

   Эта программа ззпишет содержимое буфера

  по адресу 32768 на последний,159-й трек.

  Так можно, например,сохранить ранее счи-

  танный каталог на случай его порчи. Про-

  читав его затем из 159-го трека  и запи-

  сав на 0-й,можно спасти ценные программы

  Следует  заметить еще  одну  особенность

  команд #05 и #06 - они могут работать не

  только с секторами длиной 256 байт, но и

  с секторами другой длины  (128,512,1024

  байт).Используя особенность,несложно ор-

  ганизовать обмен информацией между   ZX-

  SPECTRUM и другими компьтерами. так мож-

  но читать и писать на диски,используемые

  в компьютерах "СПЕЦИАЛИСТ" ,"ОРИОН-128",

  ДВК (с контроллером MY), а также IBM.од-

  хако в  этих случаях нельзя сразу прочи-

  тать  или записать группу секторов , так

  как секторы длиннее 256 байт  будут нак-

  ладываться в  памяти компьютера  один на

  другой , неудача также постигнет вас при

  попытке считать с диска IBM сектор с но-

  нером 9 - таких там нет (при стандартном

  формате). Выход из этой ситуации прост -

  необходимо  изучить  структуру  "чужого"

  диска и читать  (или писать)  информацию

  по одному  сектору , каждый раз вычисляя

  номер следующего трека и сектора,а также

  адрес буфера.


   5.C = #07. Вывод каталога диска.В реги-

   стре A - номер канала  для вывода ката-

   лога. Предварительно должна быть выпол-

   нена команда #18.


      пример 8.


          LD C,#18     ;код команды

          CALL #3D13   ;

          LD A,#02     ;номер канала

          LD C,#07     ;код команды

          CALL #3D13

          RET


   Эта программа равносильна директиве CAT

  TR-DOS.Если  в  регистр A перед  вызовом

  команды  занести #03 , то каталог  будет

  выведен  на принтер.

   6.C = #08.Чтение информации о файле.В

  регистре A предварительно помещается по-

  рядковый номер файла (0-127).Из каталога

  диска 16 байт переносятся в область сис-

  темных переменных TR-DOS.Не имеет значе-

  ния,стерт ли этот файл или нет и сущест-

  вует ли он вообще.Адреса соответствующих

  системных переменных приведены ниже.


   HEX    DEC    Длина,  назначение

                 байт

 

  #5CDD  23733  8       Имя файла в ASCII

   ...    ...

   #5CE4  23780

   #5CE5  23781  1       Тип файла (A,B,C,

                         D или #) , но , в

                         принципе , может

                         быть  любой  сим-

                         вол,что открывает

                         дополнительные

                         возможности

   #5CE6  23782  2       Стартовый  адрес

                         кодового  файла

                         или  длина  BASIC

                         программы

   #5CE8  23784  2       Длина  файла

   #5CEA  23786  1       Объем  файла  в

                         секторах

   #5CEB  23787  1       Номер  первого

                         сектора  файла

   #5CEC  23788  1       Номер  первого

                         трека файла.

                        

   7.C = #09.Запись информации о файле в

  каталог.Из области системных переменных

  16 байт переписываются в каталог на мес-

  то информации о файле,номер которого за-

  дан в A.


      Пример 9.


          LD A,#20

          LD C,#80

          CALL #3D13

          LD HL,#5CDD

          LD (HL),#41

          LD C,#09

          CALL #3D13

          RET


   Эта программа изменит первый символ в

  имени 32-го файла "A".Если этот файл был

  стертым, то он  "восстановится" , но для

  этого  необходимо  дополнительно  внести

  изменения  также в  8-й  сектор нулевого

  трека.Как это сделать , будет рассказано

  ниже.


   8.C = #0A.Поиск файла  по имени ,задан-

  ному в области системных переменных.

  Номер найденного файла возвращается в

  регистр C.Можно искать файлы по имени

  и типу (9 байт) или по любому  количест-

  ву первых байтов имени (от 1 до 16),сле-

  дует лишь указать  количество символов в

  системной переменной  по  адресу  #5D06

  (23814).


   9.C = #0B.Запись на диск файла.Имя и

  тип файла - в области системных перемен-

  ных,длина - в DE,начальный адрес - в HL.


      Пример 10.


          LD HL,NAME   ;адрес имени файла

          LD C,#13     ;код команды

          CALL #3D13

          LD DE,#1B00

          LD HL,#4000

          LD C,#0B     ;код команды

          CALL #3D13

          RET

   NAME   DEFM "SCR    C" ;имя и тип фай-

                           ла - 9 байт.


   Эта программа перенесет имя и тип файла

  "SCR" CODE (вообще-то переносится 16 , а

  не 9 байт , но последние 7 байт в данном

  случае не имеют значения) в области сис-

  темных переменных  (адреса 23773-23781),

  затем запишет файл на диск.


   10.C = #0C.Запись BASIC программы.Имя и

  тип файла (при типе,отличном от "B",файл

  записывается  под именем "boot") - в об-

  ласти системных переменных.


   11.C = #0E.Чтение / проверка  файла.Имя

  и тип файла - в области системных  пере-

  менных.Адрес  загрузки  файла  берется с

  директории при A = #00  или из регистро-

  вой пары DE при A = #03. При A = #FF ко-

  ды загружаются с адреса HL, причем длина

  загружаемого файла берется из DE.Байт по

  адресу 23801 определяет тип операции:

  #00 - LOAD,#FF - VERIFY.


      Пример 11.


   Предположим,что на диске есть  экранный

  файл с начальным  адресом 16384 и длиной

  6912 байт.


          LD HL,M1

          LD C,#13

          CALL #3D13

          LD C,#0A

          CALL #3D13

          LD A,C

          LD C,#08

          CALL #3D13

          LD A,#00

          LD (#5CF9),A

          LD C,#0E

          CALL #3D13

          RET

   M1     DEFM "SCR   C"


   Эта программа перенесет  имя и тип фай-

  ла "SCR" CODE (9 символов) в облать сис-

  темных переменных  (адреса 23773-23781),

  затем отыщет этот файл в каталоге , про-

  читает информацию о нем из каталога дис-

  ка,а затем прочитает сам файл по данным,

  взятым из каталога.


      Пример 12.


   Добавьте к программе из примера 11 сле-

  дующие строки:


          LD A,#03

          LD HL,#4800

          LD DE,#0800


   Эти строки следует вставлять между


    LD (#5CF9),A и LD C,#0E.


   Запустив теперь программу, вы увидите в

  центре экрана то,что раньше было в верх-

  ней его части.


      Пример 13.


   Если мофифицировать пример 11 следующим

  образом:


    LD A,#FF

    LD HL,#8000


   То программа загрузит весь файл "SCR"

  CODE по адресу 32768.

   Примечание: пример 13 следует вставлять

  в пример 11 между строками:


   LD (#5CF9),A и LD C,#0E.


   12. C = #12.Удаление файлов. Имя  и тип

               файла - в области системных

               переменных. удаляются все

               файлы с такими данными.


      Пример 14.


          LD HL,M1

          LD C,#12

          CALL #3D13

          LD C,#0A

          CALL #3D13

          LD A,C

          LD C,#18

          CALL #3D13

          RET

   M1     DEFM "SCR      C"


   Эта программа найдет на диске файл

  "SCR" CODE и сотрет его.


   13. C = #13.По адресу 23773 переписыва-

               ются 16 байт  информации из

               памяти,адресуемой регистро-

               вой парой HL.


   14. C = #14.Переписываются 16  байт ин-

               формации из области систем-

               ных  переменных  по  адресу

               23773  в  память по адресу,

               указанному в регистровой

               паре HL.


   15. C = #15.Проверка дорожки. Регистр D

               должен содержать номер про-

               веряемого физического трека


   16. C = #16.Загрузка  системного регис-

               тра.Код - в регистре A.Пре-

               дварительно к  нему прибав-

               ляется #3C.

 

   17. C = #17.Выбрать нижнюю сторону.


   18. C = #18.Настройка на диск. Проверя-

  ется тип диска  (8-й сектор директории).

  Точка  #3D2E  открывает  доступ  к любой

  подпрограмме TR-DOS,однако ее  использо-

  вание немного сложнее, чем #3D13.Для на-

  чала вспомним, как микропоцессор Z80 вы-

  полняет команды  CALL_adr и RET. При вы-

  полнении команды CALL_adr в стек записы-

  ваются два байта - адрес команды,следую-

  щей за CALL_adr, затем выполняется пере-

  ход по адресу adr,При выполнении команды

  RET из стека считываются два байта - ад-

  рес, по которому  происходит  возврат из

  подпрограммы.Причем совсем не обязатель-

  но,чтобы эти два  байта были  записаны в

  стек при выполнении команды CALL_adr-они

  могут быть  помещены туда, например, при

  выполнении команды PUSH_dd, где dd - лю-

  бая пара  регистров  микропроцессора.При

  обращении по адресу,соответствующему

  точке входа  в  DOS  ( в  данном  случае

  #3D2E), как  уже  упоминалось,происходит

  подмена основного  ПЗУ  на ПЗУ  TR-DOS.В

  последнем случае по адресу #3D2E записан

  код команды RET (#C9).Если предваритель-

  но  поместить  в стек  адрес  какой-либо

  подпрограммы TR-DOS, то  будет  выполнен

  переход на нее. В стек также  необходимо

  перед этим  поместить  адрес возврата из

  подпрограммы.


      Пример 15.


          LD HL,RET1   ;адрес возврата

          PUSH HL      ;помещаем в стек

          LD HL,ADRESS ;адрес подпрограм-

                        мы TR-DOS

          PUSH HL      ;помещаем в стек

          JP #3D2E     ;переход

   RET1   RET          ;точка для возврата


   Для использования точки входа #3D2E не-

  обходимо знать адреса подпрограмм TR-DOS

  К сожалению, TR-DOS написана очень запу-

  танно  и  дизассемблировать  ее довольно

  сложно.Три приведенных ниже отрывка мож-

  но использовать при разработке  приклад-

  ных программ,работающих с диском.Они со-

  ответствуют версии 5.03, в других верси-

  ях они тоже  есть, но могут иметь другие

  адреса.


       #2A53 (10835):

             OUT (C),A

             RET


       #3FCA (16330):

         M1  IN A,(#FF) ;читаем системный

                         регистр

             AND #C0    ;проверяем готов-

                         ность данных

             JR Z,M1    ;еще раз,если не

                         готово

             RET M      ;возврат,если все

                         записано

             OUTI       ;вывести следующий

                         байт в регистр

             JR M1      ;данных


       #3FE5 (16357):

         M2  IN A,(#FF)

             AND #C0

             JR Z,M2

             RET M      ;возврат,если все

                         прочитано

             INI        ;прочитать следую-

                         щий байт из ре-

             JR M2      ;гистра данных


   Первая из  приведенных подпрограмм поз-

  воляет выводить необходимые данные в лю-

  бой из портов  контроллера  TR-DOS  (это

  можно  сделать только через  обращение к

  ПЗУ TR-DOS).Таких портов пять.Первые че-

  тыре  из  них  принадлежат  микросхеме

  КР1818ВГ93 (табл.1).


      Таблица 1.


   Адрес     Назначение


   #1F       Регистр команд/состояния


   #3F       Регистр дорожки


   #5F       регистр сектора


   #7F       регистр данных


   Назначение регистров и структура команд

  КР1818ВГ93  рассмотрены  в  работе [4].

   Есть еще один порт, который имеет адрес

  #FF и обычно реализуется на  К555ТМ9.Это

  системный регистр  TR-DOS. При необходи-

  мости узнать  содержимое этого  регистра

  можно,воспользовавшись ячейкой 23830,ко-

  торая хранит копию этого регистра.

   Назначение  разрядов управляющего слова

  следующее:


   D0 и D1 - номер НГМД (0-A,1-B,2-C,3-D);

   D2 - команда сброса контроллера;

   D3 - готовность головки (1 - готово);

   D4 - сторона диска (0 - низ,1 - верх);

   D6 - плотность записи (0 - двойная,1 -

        одинарная);


   При чтении из порта #FF считываются си-

  гналы DRQ (готовность данных для переда-

  чи, 38  вывод ВГ93, 6-й разряд  порта) и

  INTRQ (выполнение команды,38 вывод ВГ93,

  7-й разряд порта).

   Две последние  подпрограммы  предназна-

  чены для чтения массива данных в регистр

  данных.Их можно использовать так:


      Пример 16.


          LD A,#10     ;номер трека

          LD C,#02     ;код команды

          CALL #3D13   ;установить головку

          LD A,#50     ;данные для систем-

                        ного регистра

          LD C,#FF     ;адрес порта

          LD HL,RET0   ;адрес возврата

          PUSH HL      ;в стек

          LD HL,#2A53  ;адрес подпрограммы

          PUSH HL      ;в стек

          JP #3D2E

   RET0   LD C,#1F     ;адрес порта регис-

                        тра команд

          LD A,#E4     ;код команды чтения

                        трека

          LD HL,RET1   ;адрес возврата

          PUSH HL      ;в стек

          LD HL,#2A53  ;адрес подпрограммы

          PUSH HL      ;в стек

          JP #3D2E

   RET1   LD C,#7F     ;адрес порта регис-

                        тра данных

          LD HL,RET2   ;адрес возврата

          PUSH HL      ;в стек

          LD HL,#3FE5  ;адрес подпрограммы

          PUSH HL      ;в стек

          LD HL,#A000  ;адрес буфера

          JP #3D2E

   RET2   RET


   Эта программа прочитает содержимое вер-

  хнего трека 16-го цилиндра диска со все-

  ми метками в буфер по адресу #A000.

   Если изменить две строки


          LD A,#F4     ;код команды записи

                        трека

          LD HL,#3FCA  ;адрес подпрограммы

   ( Эту программу необходимо поместить за

  место строк LD A,#E4 и LD HL,#3FE5 ), то

  получим программу записи  трека со всеми

  метками (форматирование трека).Предвари-

  тельно в  буфере  необходимо подготовить

  массив для записи. Причем можно  записы-

  вать  треки  с  секторами  любой   длины

  (128,256,512,1024 байт) и с произвольной

  нумерацией - необходимо  лишь  правильно

  подготовить данные [4]. Можно предложить

  еще одно применение  этой  точки входа.В

  ПЗУ TR-DOS довольно много свободного

  места (около 4Кбайт). Туда  можно помес-

  тить свои  программы и запустить их, как

  предложено  в примере 15. Таким образом,

  можно значительно расширить  возможности

  TR-DOS, причем такое изменение  не отра-

  зится  на совместимости. Например, можно

  добавить  программу "восстановления" или

  проверки диска,небольшую "boot"-програм-

  му или даже драйвер принтера,хотя в пос-

  леднем  случае потребуются небольшие (не

  более 20 байт) изменения в основном ПЗУ.

  Конечно,неплохо было бы внести изменения

  в некоторые подпрограммы ПЗУ TR-DOS,хотя

  бы с целью исправления имеющихся ошибок,

  но это не всегда приемлемо.Любое измене-

  ние может привести к тому, что какая-ни-

  будь  программа  перестанет  работать на

  таком  компьютере. Особенно это касается

  основного ПЗУ компьютера. Там  даже  не-

  большое изменение (в том числе и  запол-

  нение свободного пространства, изменение

  шрифта или даже надписи,которая выводит-

  ся при включении компьютера) может ухуд-

  шить совместимость с его "фирменным бра-

  том".Так что,решайте, какой компьютер вы

  хотите иметь: Sinclair - совместимый или

  Sinclair - подобный.

 






ТR-D0S для тех кто о нем ничего не знает - К0ДЫ 0ШИБ0К ПPИ ВЫЗ0ВE ИЗ ВАSIСа.

<b>ТR-D0S для тех кто о нем ничего не знает</b> - К0ДЫ 0ШИБ0К ПPИ ВЫЗ0ВE ИЗ ВАSIСа.

 TR-DOS для тех кто о нем ничего не знает.           .BIG BRAIN


WARL0CK  15.02.97


 Я  ни  разу  не  писал  статей  в HACKER и, чтобы исправить сие

недоразумение я решил написать серию статей о TR-DOSe. Начну я с

самых  азов,  т.е. с того как его вызвать из BASIC и из машинных

кодов и др.


----------------------------------------------------------------


 КОДЫ ОШИБОК ПРИ ВЫЗОВЕ ИЗ BASICa.


 Коды  ошибок  можно  получить  из переменной TR-DOS (23823) или

через переменную BASICa следующим образом:


    LET err=USR 15б19:REM:команда DOS


      или


    RAND0MIZE USR 15б19:REM:команда DOS

    LET err=РЕЕК 23823


    а теперь значения кодов ошибок:


     0 - нет ошибок

     1 - нет файла

     3 - файл с таким именем на диске уже есть

     4 - каталог переполнен (кол-во файлов больше 128)

     5 - переполнение номера записи

     б - нет диска

     7 - ошибка на диске

     8 - ошибка синтаксиса

     9 - я не нашел нигде данных на эту ошибку

    10 - канал уже открыт

    11 - диск не форматирован

    12 - канал не открыт


----------------------------------------------------------------


 СТРУКТУРА ЗАГОЛОВКА ФАЙЛА



  0...7 8 9   В   D E F

  ХХХХХ Х ХХ  ХХ  Х Х Х

  ¦     ¦ ¦   ¦   ¦ ¦ L 1 байт -номер начальной дорожки

  ¦     ¦ ¦   ¦   ¦ L-- 1 байт -номер начального сектора

  ¦     ¦ ¦   ¦   L---- 1 байт -длина файла в секторах

  ¦     ¦ ¦   L-------- 2 байта-длина файла для C0DE и програм-

  ¦     ¦ ¦               ной части для BASICa,в байтах

  ¦     ¦ L------------ 2 байта-начальный адрес для C0DE или

  ¦     ¦                 полная длина файла для BASICa

  ¦     L-------------- 1 байт -тип файла

  L-------------------- 8 байт -имя файла


----------------------------------------------------------------


 СТРУКТУРА СИСТЕМНОГО CEKT0PA


г==========T============================================¬

¦СМЕЩЕНИЕ  ¦ НАЗНАЧЕНИЕ                                 ¦-

¦==========+============================================¦-

¦ 225      ¦ Байт. Номер следующего свободного сектора. ¦-

¦          ¦ После форматирования равен нулю.           ¦-

¦----------+--------------------------------------------¦-

¦ 22б      ¦ Байт. Номер следующей свободной дорожки.   ¦-

¦          ¦ После форматирования равен единице.        ¦-

¦----------+--------------------------------------------¦-

¦ 227      ¦ Байт. Тип дискеты:                         ¦-

¦          ¦    22 - 80 дорожек,2 стороны               ¦-

¦          ¦    23 - 40 дорожек,2 стороны               ¦-

¦          ¦    24 - 80 дорожек,1 сторона               ¦-

¦          ¦    25 - 40 дорожек,1 сторона               ¦-

¦----------+--------------------------------------------¦-

¦ 228      ¦ Байт. Кол-во файлов на диске, в том числе  ¦-

¦          ¦ и удаленные.После форматирования равен 0.  ¦-

¦----------+--------------------------------------------¦-

¦ 229,230  ¦ Слово. Кол-во свободных секторов.          ¦-

¦          ¦ После форматирования:                      ¦-

¦          ¦                                            ¦-

¦          ¦  2544 - для 80 дорожечного двухстороннего  ¦-

¦          ¦  12б4 - для 80 дорожечного одностороннего  ¦-

¦          ¦           и 40 дорожечхого двухстороннего  ¦-

¦          ¦  б24  - для 40 дорожечного одностороннего  ¦-

¦----------+--------------------------------------------¦-

¦ 231      ¦ Байт. Кол-во секторов на дорожке.          ¦-

¦          ¦ Если не равен 1б то выдается сообщение:    ¦-

¦          ¦  Disk еггог и 23823=11                     ¦-

¦----------+--------------------------------------------¦-

¦ 232,233  ¦ Два байта нулей. Только вот зачем, я не    ¦-

¦          ¦ знаю.                                      ¦-

¦----------+--------------------------------------------¦-

¦ 234...242¦ Девять байт пробелов (код 32).             ¦-

¦----------+--------------------------------------------¦-

¦ 243      ¦ Один байт равный нулю.                     ¦-

¦----------+--------------------------------------------¦-

¦ 244      ¦ Байт. Кол-во удаленных файлов. После фор-  ¦-

¦          ¦ матирования равен нулю.                    ¦-

¦----------+--------------------------------------------¦-

¦ 245...252¦ 8 байт. Имя диска                          ¦-

¦----------+--------------------------------------------¦-

¦ 253...255¦ 3 байта нулей.                             ¦-

L==========¦============================================--

  --------------------------------------------------------

----------------------------------------------------------------


 ВХОДНЫЕ ТОЧКИ  TR-DOS


 0   входных   точках   TR-DOS  написано  не мало  но  я  решусь

повториться, т.к. возможно у кого-то этой информации нет.


    #3D00 - вход в DOS из BASICa.

    #3D03 - вызов команды TD-DOS из BASICa.

    #ЗDOб - канал связи с дисковым файлом-программа ввода.

    #ЗDOE - канал связи с дисковым файлом-программа вывода.

    #3D13 - выполнение команды заданноы в регистре С процессора.

    #3D2F - переход на любой адрес DOS.


 По  адресу  #3D2F  стоят две комманды :NOP и RET.С помощью этой

точки входа можно попасть по любому адресу ПЗУ TR-DOS.


    LD    HL,LAB   ; адрес возврата

    PUSH  HL       ; заносим его на стек

    LD    HL,адрес ; адрес в ПЗУ TR-DOS

    PUSH  HL       ; заносим его на стек

    JP    #3D2F    ; переход на точку входа

LAB .....          ; продолжение программы


      или


    LD    HL,адрес ; адрес ПЗУ TR-DOS

    CALL  DOS      ; заносим на стек адрес возврата

    .....          ; продолжение программы

DOS PUSH  HL       ; заносим на стек адрес в ПЗУ TR-DOS

    JP    #3D2F    ; переход на точку входа



 Наиболее  важной  точкой  входа  является #3D13.Теперь разберем

команды, которые можно использовать через эту точку входа.

 Чтобы   вызвать  команду  нужно  задать  необходимые  данные  в

регистрах и воспользоваться данной конструкцией:


    LD     С,номер ;номер команды

    CALL   #3D13   ;переход на точку входа

    .....          ;продолжение программы


==========T=====================================================

Значение  ¦Краткое описание команды

регистра С¦

==========+=====================================================

  #00     ¦ Восстановление ВГ-93: головка отводится на нулевую

          ¦ дорожку и ожидает сигнала INTRQ. Ожидание можно

          ¦ прервать нажав BREAK.

----------+-----------------------------------------------------

  #01     ¦ Инициализация дисковода, заданного в регистре А:

          ¦   00-А

          ¦   01-В

          ¦   02-С

          ¦   03-D

----------+-----------------------------------------------------

  #02     ¦ Установка головки на дорожку номер, которой задан в

          ¦ регистре А (0...179).

----------+-----------------------------------------------------

  #03     ¦ Установка номера сектора, номер которого задан в

          ¦ регистре А (1..1б).

----------+-----------------------------------------------------

  #04     ¦ Установка адреса буффера, заданного в регистровой

          ¦ паре HL. Помещается в системную переменную #5D00.

----------+-----------------------------------------------------

  #05     ¦ Чтение блока секторов. По адресу HL считывается В

          ¦ секторов с доржки D,первый из которых имеет номер E.

----------+-----------------------------------------------------

  #0б     ¦ Запись блока секторов. Параметры те же, что и в ко-

          ¦ манде #05.

----------+-----------------------------------------------------

  #07     ¦ Вывод каталога диска в канал с номером А.

----------+-----------------------------------------------------

  #08     ¦ Чтение заголовка (дискриптора) файла в область сис-

          ¦ темных переменных. Номер файла в регистре А (0..127

          ¦ в том числе и удаленные).

----------+-----------------------------------------------------

  #09     ¦ Запись заголовка файла на диск. Номер файла в А.

----------+-----------------------------------------------------

  #0А     ¦ Поиск файла в каталоге по имени и типу, которые за-

          ¦ даны в системных переменных #5CDD...#5CE5; кол-во

          ¦ проверяемых байтов заносится в переменную 23814,

          ¦ обычно 9. Если файл был найден, то его номер в ре-

          ¦ гистре С и ячеыках 23838, 23823; если не найден то

          ¦ регистр С=255, 23823=255, 23838 не изменяется.

----------+-----------------------------------------------------

  #0В     ¦ Запись на диск файла типа C0DE с адреса HL и длиной

          ¦ DE; имя и тип файла должны находиться в системных

          ¦ переменны. Проверка на наличие одноименного файла не

          ¦ проводитяся, так что можно создать несколько файлов

          ¦ с одинаковым именем и типом.

----------+-----------------------------------------------------

  #0С     ¦ Запись BASIC программы. Имя задается в системных пе-

          ¦ ременных. Проверка на наличие одноименного файла не

          ¦ производится. В переменной #5CD1 задается номер

          ¦ строки автостарта. Если тип не В, то файл запишеться

          ¦ под именем "boot".

----------+-----------------------------------------------------

  #0E     ¦ Загрузка или VERIFY файлов; тип и заголовок в сис-

          ¦ темных переменных.При 23801=0 производится загрузка,

          ¦ а при 255 проверка. Для загрузки BASIC программы

          ¦ 23824 должна быть равна 0, если 255 то старая прог-

          ¦ памма стирается, а новая не загружается. Данные, в

          ¦ зависимости от содержимого А, задаются по разному:

          ¦

          ¦   А=0   ;адрес и длина берутся из каталога

          ¦   А=3   ;адрес в HL, длина DE

          ¦   А=255 ;адрес в HL, длина из каталога

----------+-----------------------------------------------------

  #12     ¦ Стирается файл, имя и тип которого заданны в сис-

          ¦ темных переменных, стерты будут все файлы с такими

          ¦ именем и типом, их кол-во в переменной 23815.

----------+-----------------------------------------------------

  #13     ¦ Перенос информации о файле (дискриптора,1б байт) с

          ¦ адреса HL в системные переменные #5CDD...#5CE5.

----------+-----------------------------------------------------

  #14     ¦ Перенос дискриптора файла из системных переменных

          ¦ в адрес HL.

----------+-----------------------------------------------------

  #15     ¦ Проверка дорожки, номер которой задан в регистре D.

          ¦ Если на дорожке будут найдены битые сектора, то в

          ¦ системных переменных будут данные:

          ¦     #5DOF код ошибки 7

          ¦     #SСDб кол-во битых секторов

----------+-----------------------------------------------------

  #1б     ¦ Устанавливает текущую сторону диска 0.

----------+-----------------------------------------------------

  #17     ¦ Устанавливает текущую сторону диска 1.

----------+-----------------------------------------------------

  #18     ¦ Производит настройку переменных на тип дискеты.

==========¦=====================================================


 Приведу несколько примеров использования точки входа #3D13:



Загрузчик моноблок :


; в BASIC строке за REM расположена примерно такая процедура:


    LD     HL,#9С40   ;адрес загрузки картинки

    LD     ВС,#1В05   ;кол-во секторов и код команды 5

    LD     DE,(#5CF4) ;номер следующих дорожки и сектора

    CALL   15б35      ;вызов процедуры загрузки

    LD     DE,#4000   ;адрес экранной области

    LD     HL,#9С40   ;адрес куда загрузили картинку

    LD     ВС,#1В00   ;длина картинки

    LDIR              ;переброска картинки в экран

    LD     HL,#7530   ;адрес загрузки основного блока

    LD     ВС,#б205   ;кол-во секторов и код команды 5

    LD     DE,(#5CF4) ;номер следующих дорожки и сектора

    CALL   15б35      ;вызов процедуры загрузки

    JP     #7530      ;запуск загруженной программы



Пофайловый загрузчик:


; так же в строке после REM небольшая процедура:


          LD     HL,BL0CK1 ;адрес имени и типа первого блока

          LD     С,#13     ;перенос дискриптора

          CALL   15б35     ;вызов процедуры переноса дискриптора

          LD     С,#0А     ;поиск фаила на диске

          CALL   15б35     ;вызов процедуры поиска

          LD     А,С       ;сохраняем номер файла в аккумуляторе

          INC    С         ;если файла нет,

          JP     Z,LABEL   ;  то переход на вывод сообщения

          LD     С,8       ;чтение дискриптора файла

          CALL   15б35     ;переход на точку входа

          X0R    А         ;загрузка

          LD     (23801),А ;  файла

          LD     А,255     ;длина из каталога адрес из HL

          LD     HL,#9С40  ;адрес загрузки

          PUSH   HL        ;запоминаем его

          LD     С,#0E     ;загружаем первый блок

          CALL   15б35     ;вызов процедуры загрузки

          POP    HL        ;восстанавливаем адрес

          LD     DE,#4000  ;переброска

          LD     ВС,#1В00  ;  картинки

          LDIR             ;  в экран

          LD     HL,BL0CK2 ;адрес имени второго блока

          LD     С,#13     ;перенос дискриптора файла

          CALL   15б35     ;вызов процедуры переноса

          LD     С,#0А     ;поиск файла на диске

          CALL   15б35     ;вызов процедуры поиска

          LD     А,С       ;сохраняем номер фаила в аккумуляторе

          INC    С         ;если файл не найден

          JP     Z,LABEL   ;  то переход на печать сообщения

          LD     С,#08     ;загрузка дискриптора файла

          CALL   15б35     ;переход в TR-DOS

          X0R    А         ;загрузка

          LD     (23801),А ;  блока

          LD     А,255     ;адрес в HL, длина из каталога

          LD     HL,#7530  ;адрес загрузки

          LD     С,#0E     ;загрузка файла

          CALL   15б35     ;вызов процедуры загрузки

          JP     #7530     ;запуск основного блока

LABEL     CALL   3435      ;очистка экрана

          LD     А,2       ;номер канала вывода

          CALL   #1б01     ;вызов процедуры открытия канала

          LD     DE,ТЕХТ   ;DE начало сообщения

          LD     ВС,BL0CK1-ТЕХТ  ;ВС длина сообщения

          CALL   8252      ;печать сообщения

          LD     А,2       ;красный цвет

          0UT    (254),А   ;  бордюра

          DI               ;запрещаем прерывания

          HALT             ;вешаем комп

ТЕХТ      DEFB 22,0,0      ;координаты печати

          DEFM "FILE N0T FOUND!"

BL0CK1    DEFM "FILE1   С"

BL0CK1    DEFM "FILE2   С"


 Раз уж разговор зашел о точках входа то я дам несколько адресов

процедур TR-DOS с адресами для версии ПЗУ 5.04Т.


==============T=================================================

 адрес (НЕХ)  ¦ назначение

==============+=================================================

     0000     ¦ Полный рестарт системы

--------------+-------------------------------------------------

     0008     ¦ RST #08, инициализация, в DE вершина памяти.

--------------+-------------------------------------------------

     0010     ¦ RST #10,вывод символа из аккумулятора.

--------------+-------------------------------------------------

     0018     ¦ RST #18, строки символов начиная с (HL), кончая

              ¦ нулем или символом больше 127.

--------------+-------------------------------------------------

     0020     ¦ RST #20, вызов процедуры из ПЗУ, адрес сразу за

              ¦ RST #20, например:

              ¦

              ¦       RST  #20

              ¦       DEFW #ODбВ

--------------+-------------------------------------------------

     0028     ¦ RST #28, возвращает в HL адрес текущего канала

              ¦ с учетом сдвига в С.

--------------+-------------------------------------------------

     OOбб     ¦ Обработчик MAGIC кнопки.

--------------+-------------------------------------------------

     01D3     ¦ Выход из DOS после выполнения команд #0D-#11

              ¦ точки входа 15б35. Это команды для дальнейшего

              ¦ расширения TR-DOS.

--------------+-------------------------------------------------

     03б0     ¦ Название версии TR-DOS.

--------------+-------------------------------------------------

     0405     ¦ Процедура выполнения команды #18.

--------------+-------------------------------------------------

     0800     ¦ Свободная память (#FF).

--------------+-------------------------------------------------

     1003     ¦ Текст "Подсоединен интерфейс 1".

--------------+-------------------------------------------------

     10А5     ¦ Текст системной информации для LIST.

--------------+-------------------------------------------------

     115D     ¦ Печать листа (HL) в десятичной форме.

--------------+-------------------------------------------------

     1бSС     ¦ Выполнение команды #08.

--------------+-------------------------------------------------

     1ббЧ     ¦ Выполнение команды #09.

--------------+-------------------------------------------------

     1CF0     ¦ Выполнение командв #0А.

--------------+-------------------------------------------------

     1EЗD     ¦ Выполнение команды #05.

--------------+-------------------------------------------------

     1EЧD     ¦ Выполнение команды #0б.

--------------+-------------------------------------------------

     1FFD     ¦ Форматирование дорожки

              ¦ в E номер дорожки

              ¦ в системной переменной #SСEб адрес таблицы

              ¦ в системной переменной #5CE8 адрес таблицы +1

              ¦ в системной переменной #5CD7 флаг проверки

              ¦ секторов #1FB9 - 0rdinary, #325А - TURB0.

--------------+-------------------------------------------------

     1FEB     ¦ Выполнение команды #1б.

--------------+-------------------------------------------------

     1FFб     ¦ Выполнение команды #17.

--------------+-------------------------------------------------

     2739     ¦ Выполнение команды #15.

--------------+-------------------------------------------------

     27бб     ¦ Тексты сообщений TR-DOS.

--------------+-------------------------------------------------

     283С     ¦ Сюда переходит управление с 15б35, определение

              ¦  команды.

--------------+-------------------------------------------------

     288С     ¦ Таблица адресов команд адресуемых регистром С.

--------------+-------------------------------------------------

     28D8     ¦ Выполнение команды #07.

--------------+-------------------------------------------------

     28E0     ¦ Выполнение команды #13.

--------------+-------------------------------------------------

     28E3     ¦ Выполнение команды #14.

--------------+-------------------------------------------------

     28E5     ¦ Перенос дискриптора файла

              ¦ при А=0 из HL в системные переменные

              ¦ при А<>0 наоборот.

--------------+-------------------------------------------------

     28F2     ¦ Выполнение команды #0С.

--------------+-------------------------------------------------

     28FB     ¦ Выплонение команды #0В.

--------------+-------------------------------------------------

     290F     ¦ Выполнение команды #0E.

--------------+-------------------------------------------------

     292б     ¦ Выполнение команды #12.

--------------+-------------------------------------------------

     29В1     ¦ Тексты сообшений TR-DOS.

--------------+-------------------------------------------------

     2А53     ¦ Вывод А в порт ВС.

--------------+-------------------------------------------------

     2АSб     ¦ Сюда TR-DOS переходит после нажатия MAGIC.

--------------+-------------------------------------------------

     ЗOFD     ¦ Ключевые слова TR-DOS.

--------------+-------------------------------------------------

     2FFЗ     ¦ Таблица адресов ключевых слов.

--------------+-------------------------------------------------

     3D00     ¦ Точка входа. См. начало статьи.

--------------+-------------------------------------------------

     3D03     ¦ Точка входа.

--------------+-------------------------------------------------

     ЗDOб     ¦ Точка входа.

--------------+-------------------------------------------------

     ЗDOE     ¦ Точка входа.

--------------+-------------------------------------------------

     3D13     ¦ Точка входа.

--------------+-------------------------------------------------

     3D24     ¦ Точка входа.

--------------+-------------------------------------------------

     3D2F     ¦ Точка входа.

--------------+-------------------------------------------------

     3D98     ¦ Выполнение команды #00.

--------------+-------------------------------------------------

     3DCB     ¦ Выполнение команды #01.

--------------+-------------------------------------------------

     3E44     ¦ Выполнение команды ВГ93 из В, данные в А.

--------------+-------------------------------------------------

     ЗEбЗ     ¦ Выполнение команды #02.

--------------+-------------------------------------------------

     3F02     ¦ Выполнение команды #03.

--------------+-------------------------------------------------

     ЗFOб     ¦ Выполнение команды #04.

--------------+-------------------------------------------------

     ЗEF5     ¦ Ожидание выполнения последней команды ВГ93.

 -------------+-------------------------------------------------

     ЗFE5     ¦ Здесь находится процедура приема группы байт

              ¦ от ВГ93 (INI).

 -------------+-------------------------------------------------

     3FCA     ¦ Здесь находится процедура блочной записи (0UTI).

==============¦=================================================


 Самый  простой  и надежный способ определения версии ПЗУ TR-DOS

это через команду #13:


   LD     HL,#03б0  ;в HL помещаем адрес версии TR-DOS

                    ;  (для 5.04Т)

   LD     С,#13     ;перенос из HL в системные переменные

                    ;  1б байт

   CALL   15б35     ;переход в TR-DOS

   .....            ;процедура сравнения стрингов



 Я думаю, что для первой статьи в HACKER этого хватит. Ну чтож я

заканчиваю.  Большинство информации я взял из книжки "TR-DOS для

это через команду #13:


   LD     HL,#03б0  ;в HL помещаем адрес версии TR-DOS

                    ;  (для 5.04Т)

   LD     С,#13     ;перенос из HL в системные переменные

                    ;  1б байт

   CALL   15б35     ;переход в TR-DOS

   .....            ;процедура сравнения стрингов



 Я думаю, что для первой статьи в HACKER этого хватит. Ну чтож я

заканчиваю.  Большинство информации я взял из книжки "TR-DOS для

професионалов и любителей" Ю. Поморцева.





.SCL

SCL (*.scl) - формат для хранения TR-DOS-файлов, 

используемый многими эмуляторами ZX Spectrum. 

Отличается от формата TRD хранением только необходимых

файлов, также может хранить больше файлов, чем 

допускает система. Разработан Павлом 

Неделиным (Paul Pavlov). Стал популярным благодаря 

хранению большинства ПО на сайте Virtual TR-DOS в этом формате.

Расширение SCL является сокращением слова SINCLAIR.

Описание формата

Заголовок:

        +0, 8 байт - сигнатура SINCLAIR

        +8, 1 байт - число блоков 

Данные:

        Заголовки файлов (14 байт из каталога без track/sec):

            Заголовок 1-го файла

            Заголовок 2-го и последующих файлов (если есть) 

Данные файлов:

            Кодовый блок 1-го файла

            Кодовый блок 2-го и последующих файлов (если есть) 

Последние 4 байта - контpольная сyмма (арифметическая сумма всех предыдущих байт)








Порты

Для управления интерфейсом BETA DISC используются следующие порты:

 #1f, #3f, #5f, #7f, #ff.

регистр состояния #1f [in]

регистр команд #1f [out]

in a,(#1f) - выдает состояние контроллера, где A:

.-------------------------------------------------------

|bit |          commands                               |

|-------------------------------------------------------

|    |вспомо   |запись |чтение |чтение |запись |чтение |

|    |гательные|сектора|сектора|адреса |дорожки|дорожки|

|    |--------------------------------------------------

|0   |занято, идет выполнение команды                  |

|-------------------------------------------------------

|1   |идент.  |              запрос                    |

|    |импульс |                данных                  |

|-------------------------------------------------------

|2   |головка |               потеря                   |

|    |на 0 тр.|                данных                  |

|-------------------------------------------------------

|3   |           ошибка црц            |        0      |

|-------------------------------------------------------

|4   |ошибка  |       сектор           |       0       |

|    |поиска  |     не найден          |               |

|-------------------------------------------------------

|5   |загрузка|ошибка  |тип adr|  0    |ошибка |   0   |

|    |головки | записи | метки |       | записи|       |

|------------------------------------------------------|

|6   |защита записи    |       0       |защита |   0   |

|    |                 |               |записи |       |

|-------------------------------------------------------

|7   | готовность дисковода (1 - не готов)             |

'----------------------------------------------------snd


out (#1f),a - выполнение команды, где A - код команды (см. раздел команды).


Регистр дорожки #3f - in/out

in a,(#3f) - выдает номер текущего физического трека.

out (#3f),a - задание нового физического трека.


Регистр сектора #5f - in/out

in a,(#5f) - выдает номер текущего сектора.

out (#5f),a - задание нового сектора (нумерация с 1!).


Регистр данных #7f - in/out

in a,(#7f) - чтение байта

out (#7f),a - задание номера логического трека, 

для команды позиционирования или запись байта если 

была какая-либо команда на запись.

ini - последовательное считывание данных.

outi - последовательная запись данных.


Регистр #ff состояния сигналов intrq и drq - [in]

Системный регистр #ff - [out]

in a,(#ff) - отражает состояние сигналов intrq (d7), drq (d6) контроллера, где A:

bit

7 - intrq (сигнал окончания выполнения команды)

6 - drq (запрос данных микроконтроллером)

5 - x

4 - x

3 - x

2 - x

1 - x


out (#ff),a - вспомогательные функции, где A:

bit

7 - x

6 - метод записи (1 - fm, 0 - mfm)

5 - x

4 - выбор магнитной головки (0 - верх, 1 - низ)

3 - загрузка головки (всегда должен быть в 1)

2 - аппаратный сброс микроконтроллера (если 0)

1 - номер дисковода (00 - a, 01 - b, 10 - c, 11 - d)

0 - -/-

Порты TR-DOS доступны только тогда, когда включено ПЗУ TR-DOS! 

Включить ПЗУ TR-DOS можно через специальные точки входа.


Точки входа

Аппаратное включение дискового интерфейса происходит в

 момент выполнения какой-либо команды, записаной по адрес

у в диапазоне #3d00 - #3dff. Проще говоря, чтобы 

попасть в ПЗУ TR-DOS нужно запомнить на стеке адрес под

программы в ПЗУ TR-DOS, куда вы хотите передать управ

ление и выполнить команду RET, расположенную в этом 

диапазоне. Либо, если адрес подпрограммы лежит 

непосредственно в пределах #3d00 - #3dff, можно просто 

передать на нее управление, например, обычным CALL NN.

Чаще всего в качестве точки входа используют две:


самая распространенная точка входа:

 #3d2f nop

       ret


Редко используемая точка входа (не «переваривается» некоторыми отечественными 

версиями интерфейса: GRM Pentagon и т.п.):

#3d30 ret

Но если хорошенько «покопаться» в ПЗУ TR-DOS, то 

можно найти еще несколько (недокументированных):

 #3d2c, #3d4b, #3d7f, #3d93, #3d96, #3d97, #3dac

 По всем этим адресам находится команда ret, за исключением точки 3.


Недокументированная точка входа и кроме меня никем неиспользуемая:

#3d96 nop

      ret

По идее, возможно использование любого адреса в 

качестве точки входа из выше перечисленных, но 

гарантию работоспособности на всяких «левых» 

интерфейсах я не даю! Поэтому я настоятельно рекомендую

 использовать либо первую, либо третью точку входа.


Используя эти точки входа можно добраться до 

подпрограмм расположенных в ПЗУ TR-DOS.


Подпрограммы

С помощью обрывок подпрограмм TR-DOS можно писать

 свои собственные процедуры работы с диском. 

Для начала приведу адреса доступа к портам ввода данных:

#2fc3 out (#1f),a ;регистр команд

      ret

.....

#1e3a out (#3f),a ; регистр дорожки

      ret

.....

#1ff3 out (#ff),a ; системный регистр

      ret

.....

#2f0c out (#ff),a ; системный регистр

      ret         ; (практически не используется)

.....

#2a53 out (c),a   ; универсальный адрес

      ret


Тот же самый эффект можно получить воспользовавшись

 процедурой по адресу #20b8:

#20b8 out (c),d

      djnz #20b1

      ret


Или процедурой по адресу #3fd1:

#3fca in a,(#ff)    ; ожидание intrq или drq

      and %11000000 ; bit7 - intrq/bit6 - drq

      jr z,#3fca    ; для drq условие заведомо не выполнится

      ret m         ; выход по intrq

#3fd1 outi          ; запись байта

      jr #3fca

Как видите с вводом данных все просто и понятно, чег

о не скажешь об их получении. К сожалению в ПЗУ 

TR-DOS не предусмотрено такой возможности. Я не говорю

 о том, чтобы была такая же возможность считывать со

стояние каждого регистра в отдельности.

 В принципе, хватило бы одной простой команды 

типа in a,(c), но т.к. таковой нет, приходится 

прибегать к различного рода ухищрениям:


Так, например, чтобы прочитать значения регистров можно 

воспользоваться следующей процедурой по адресу #3ef3:

#3ef3 in h,(c)

#3ef5 in a,(#ff)    ; ожидание intrq или drq

      and %11000000 ; bit7 - intrq/bit6 - drq

      jr z,#3ef5    ; для drq условие заведомо не выполняется

      ei            ; !

      ret m         ; выход по intrq

На выходе в H будет значение состояния нужного вам регистра.

 Единственный недостаток в том, что на выходе разрешаются

 прерывания, что в некоторых случаях совсем не нужно.


Можно воспользоваться другой процедурой по адресу #3fec:

#3fe5 in a,(#ff)    ; ожидание intrq или drq

      and %11000000 ; bit7 - intrq/bit6 - drq

      jr z,#3fe5    ; для drq условие заведомо не выполняется

      ret m         ; выход по intrq

#3fec ini           ; чтение байта

      jr #3fe5

На выходе в (HL) будет значение состояния нужного вам регистра.


Таким образом можно считать состояние всех регистров

 кроме регистра состояния #1f, т.к. произойдет зацикливание

 при чтении #ff. Поэтому чтобы все же заполучить 

значение регистра состояния, нужно выполнить следующие действия:

;(c) Andrew MOA Larchenko

in_1f out (#3f),0

      out (#5f),#a

      ld d,#01

      jp #3f33

На выходе в B будет находиться значение регистра состояний #1f,

 т.е. таким способом мы проэмулировали 

команду in b,(#1f). Однако при выполнении данной процедуры 

были запорчены регистры дорожки и сектора, поэтому, 

при необходимости, следует восстановить в них исходные значения.



Команды

Прежде чем перейти непосредственно к командам, 

необходимо сказать, что выполнение каждой последующей 

команды возможно только тогда, когда завершена предыдущая, 

т.е. поступит сигнал intrq. Отследить этот сигнал можно 

воспользовавшись процедурой:

#3fe5 in a,(#ff)    ; ожидание intrq или drq

      and %11000000 ; bit7 - intrq/bit6 - drq

      jr z,#3fe5    ; для drq условие заведомо не выполнится

      ret m         ; выход по intrq

Или любой другой аналогичной процедурой по другим адресам...


Также надо отметить, что выбор дисковода и др. 

для выполнения команд осуществляется с помощью 

инструкции out (#ff),a (см. раздел порты).


Микроконтроллер может выполнять 11 команд:

1. Восстановление               - %0000hvrr - +

2. Поиск/Позиционирование       - %0001hvrr |

3. Шаг в предыдущем направлении - %001thvrr + - 

4. Шаг вперед                   - %010thvrr |

5. Шаг назад                    - %011thvrr - +

6. Чтение сектора               - %100mseca

7. Запись сектора               - %101msec0

8. Чтение адреса                - %11000e00

9. Чтение дорожки               - %11100e00

10. Запись дорожки/форматир-е   - %11110e00

11. Принудительное прерывание   - %1101iiii

где rr - скорость позиционирования головки: 

00 - 6мс, 01 - 12мс, 10 - 20мс, 11 - 30мс (1Mhz).

v - проверка номера дорожки после позиционирования.

h - загрузка головки (всегда должен быть в 1!).

t - изменение номера дорожки в регистре дорожки после каждого шага.

a - тип адресной метки (0 - #fb, 

стирание сектора запрещено 1 - #F8, стирание сектора разрешено).

c - проверка номера стороны диска при идентификации индексной области.

e - задержка после загрузки головки на 30мс (1Mhz).

s - сторона диска (0 - верх/1 - низ).

m - мультисекторная операция.

i - условие прерывания:

i0 - после перехода сигнала cprdy из 0 в 1 (готов);

i1 - после перехода сигнала cprdy из 1 в 0 (не готов);

i2 - при поступлении индексного импульса;

i3 - немедленное прерывание команды, при:

i=0 - intrq не выдается

i=1 - intrq выдается.


Восстановление - осуществляет позиционирование головки на 

дорожку 0. Если через 256 импульсов сигнал tr00 не появится, 

то команда прекращает работу. Выполняется независимо 

от готовности дисковода.

to_tr0 out (#1f),#8 ; %00001000


Поиск/позиционирование - перемещает головку на нужный трек. 

Перемещение происходит до тех пор, пока не совпадут номера 

физической дорожки в #3f и логической дорожки в #7f. 

Номер трека указывается без учета стороны (/2)!

jump out (#7f),trk/2 ; номер нужного трека/2

     out (#1f),#18   ; %00011000

     jp #3fe5         ; ожидание выполнения команды


Шаг (пункт 3) - двигает головку на один шаг в предыдущем направлении.

step out (#1f),#38 ;%00111000

     jp #3fe5      ; ожидание выполнения команды


Шаг вперед - двигает головку на один шаг вперед.

step_f out (#1f),#58 ; %01011000

       jp #3fe5      ; ожидание выполнения команды


Шаг назад - двигает головку на один шаг назад.

step_b out (#1f),#78 ; %01111000

       jp #3fe5      ; ожидание выполнения команды


Чтение сектора - чтение сектора с текущей дорожки. 

При мультисекторной операции читаются все сектора с заданного до конца трека.

load_s out (#5f),sec ; номер считанного сектора (нумерация с 1)

       out (#1f),#80 ; %10000000

       ld hl,address ; адрес загрузки

       ld c,#7f

       jp #3fe5      ; чтение сектора


Запись сектора - запись сектора на текущую дорожку. 

При мультисекторной операции записываются все сектора с 

заданного до конца трека.

save_s out (#5f),sec ; номер записываемого сектора (нумерация с 1)

       out (#1f),#a0 ; %10100000

       ld hl,address ; адрес записи

       ld c,#7f

       jp #3fca      ; запись сектора


Чтение адреса - считывает 6 байтов идентификатора сектора, включая crc (контрольная сумма).

l_addr ld hl,buffer  ; адрес загрузки идентификатора

       out (#1f),#c0 ; %11000000

       jp #3fe5      ; чтение идентификатора сектора

На выходе в (HL) по смещению:

+0 - номер трека (0 - 128)

+1 - номер стороны (0-верх, 1-низ)

+2 - номер сектора (0 - 128)

+3 - длина сектора (0-128b, 1-256b, 2-512b, 3-1024b)

+4 - контрольная сумма (?)

+5 - -/-


Чтение дорожки - считывает всю дорожку целиком. 

Синхронизация отсутствует (crc не проверяются), 

т.е. нельзя отследить правильность считанной информации.

load_t ld hl,address ; адрес загрузки трека

       out (#1f),#e0 ; %11100000

       jp #3fe5      ; чтение трека (>6Kb)


Запись дорожки/форматирование - форматирование дорожки.

format ld hl,address ; адрес расположения данных дорожки

       out (#1f),#f0 ; %11110000


Принудительное прерывание - завершение выполняемой на 

данный момент команды. В отличие от других команд она 

может выдаваться в любой момент.

inter out (#1f),i ; %1101iiii (значение i см. выше)


Вот еще две подпрограммки из ПЗУ TR-DOS.

#20af ld b,#01

#20b1 in a,(#ff)    ; ожидание intrq или drq

      and %11000000 ; bit7 - intrq/bit6 - drq

      jr z,#20b1

      ret m         ; выход если intrq

#20b8 out (c),d     ; запись байта если drq

      djnz #20b1    ; повтор пока b<>0

      ret


#3ef3 in h,(c)

#3ef5 in a,(#ff)    ; ожидание intrq или drq

      and %11000000 ; bit7 - intrq/bit6 - drq

      jr z,#3ef5

      ei            ; !

      ret m         ; выход если intrq

#3efd di            ; !

#3efe in a,(#7f)    ; чтение байта если drq

      jr #3ef5      ; повтор пока не поступит intrq


Вот, собственно и все.

Адрес для связи: Юдин Александр Владимирович (faster), 

432044, г. Ульяновск, ул. Героев Свири 16а-24.

© 2004-2013 Perspective group





Я  не буду в этой статье повторять  описания  tr-dos различных

изданий,  показывать  пример использования её из basica и пере-

числять  номера  подпрограмм пзу '#3d13'. Моя цель состоит в опи-

сании  тонкостей и сложностей, с которыми я сам встретился, пыта-

ясь  раскрутить мотор дисковода, написать  свой загрузчик и нако-

нец  свой командера. Я попытаюсь доходчиво  объяснить 'как' рабо-

тать с ВГ 93 и дисководом и приведу  примеры  полезных процеду-

рок.


             ВГ 93.


 Как  Вам  должно быть известно, общение  с  ВГ  происходит через

порты, обращаться к которым можно  _только_  из пзу tr-dos (от-

дельные экземпляры speccy позволяют  выводить/читать  из портов

хоть  из  озу,  но  то  является только  нежеланием  разработчика

привести  контроллер дисковода кстандарту).


 Порты (регистры) ВГ 93 чтение:


 #1f  -  содержит  информацию  о состоянии  микросхемы в процессе

выполнения команды или о наличии ошибок исполнения команды.

 #3f  -  содержит  номер дорожки (физический),   на  которой,  по

мнению  ВГ, стоит головка дисковода.   Если   сдвинуть  головку

вручную,  значение  регистра  не изменится!

 #5f - содержит номер сектора.

 #7f  - информация, передаваемая микросхемой  в процессе выполне-

ния команды (очередной считанный с диска байт).

 #ff  -  бит 7 =1 означает конец выполнения команды, бит 6 =1 оз-

начает  просьбу  ВГ  передать ей следующий  байт,  например,  при

записи сектора, или взять прочитанный  ею с диска байт, который

она поместила в регистр #7f.


 Практического применения чтение из портов #3f и #5f не имеет, да

из  них  и  невозможно прочитать (см. 'чтение из портов').



запись:

 #1f - код команды.

 #3f и #5f - тоже, что чтение.

 #7f  -  сюда следует записывать очередной  байт после просьбы ВГ

для  записи его на диск, или номер дорожки для команды позицио-

нирования.

 #ff  - бит 6 - плотность записи (fm/mfm). В некоторых контролле-

рах (а может и во всех) этот бит не  подключен,  поэтому не стоит

пытаться им воспользоваться. бит 3  отвечает за остановку мотора.

Если он =1, то мотор через некоторое время после выполнения ко-

манды  остановится,  иначе будет крутиться  вечно (по-моему так).

Бит 2 (небезизвестная книга Ларченко и Родионова даёт о нём не-

верную информацию) - сбросив его в  0  Вы  добьётесь  мгновенного

прекращения выполнения команды и остановки мотора. Порт #ff имеет

ещё три бита, но внимание! ВГ об их  существовании  не знает! Они

управляют непосредственно дисководом и контроллером (в частнос-

ти, ВГ не имеет понятия, с каким дисководом  (a,b...) она работа-

ет).  Бит 4 - номер стороны дискеты  (1  -  нижняя), биты 1,0 -

номер    дисковода.    Некоторые контроллеры  (а может почти все)

работают только с битом 0 и позволяют выбрать только 2 дисково-

да.


   Как программировать ВГ 93.


Как  я уже сказал, в наших программах   нельзя  непосредственно

обратиться  к регистрам ВГ - для чтения/записи в порты контролле-

ра необходимо использовать соответствующие участки пзу DOS, ко-

торые  удобно  искать  с помощью sts  4.1  (следует заметить, что

sts  4.1 неверно трассирует операции  ld  r,(hl)  в пзу tr-dos,

где r<>a и 0<=hl<=#3fff). К найденным  участкам затем можно об-

ращаться     такой    последовательностью  команд  (adr - адрес

участка):

     ld   hl,l01

     push hl

     ld   hl,adr

     push hl

     jp   #3d2f

l01 ...

Удобнее так:

     ld   ix,adr

     call dos

     ...


dos  push ix

     jp   #3d2f


 Не следует вместо #3d2f ставить #3d30,   т.к.  существуют  особо

тормозные контроллеры (?), которые  не  успевают подключить пзу

tr-dos  и требуют для этого нескольких тактов процессора.


 Полезные подпрограммы пзу DOS:


  запись в любой порт:

#2a53 out  (c),a

      ret


  запись на диск до конца операции из hl:

#3fca in   a,(#ff)

      and  #c0

      jr   z,#3fca

      ret  m; bit 7,a=1 - конец

      outi

      jr   #3fca


  чтение  в  hl с диска до конца операции:

#3fe5 тоже, только ini вместо outi.


  ожидание   выполнения  команды (используется при позиционирова-

нии):

#3ef5 in   a,(#ff)

      and  #c0

      jr   z,#3ef5

      ei

      ret  m

      di

      in   a,(#7f)

      jr   #3ef5


 Везде  в статье адреса даны для tr-dos 5.03/04t. Для 5.01 адреса

другие.


 Теперь  у нас есть всё для того чтобы заставить ВГ выполнить ко-

манду. Однако надо бы ещё проверить  успешность  этого выполне-

ния, т.е. прочитать значение регистра  #1f. Разработчики tr-dos

видимо не предполагали, что возможности  их  системы  могут  не

удовлетворять  программеров, отчасти  поэтому  такой последова-

тельности  как in a,(#1f) или in r,(c):  ret  в  пзу нет, поэтому

прочитать значения регистров #3f и #5f невозможно (хотя и не нуж-

но),  а  #1f  -  затруднительно. Есть  несколько  способов (2 или

3).  На мой взгляд самый удобный и  надёжный  такой:  сразу после

выполнения   команды  необходимо выполнить следующие дейстия:

   ld  d,номер дорожки, на которой стоит головка дисковода (его

надо знать)

   ld   (#5cd8),не 0

   ld   ix,#2740

   call dos

   ld   a,(#5ccd)

здесь a будет содержать значение регистра  #1f.  Следует  учесть,

что во время отработки процедуры #2740  осуществляется опрос кла-

виши break, поэтому во избежание недоразумений следует перехваты-

вать  попытку выхода в пзу basic (см. ниже).


 Теперь  средство  есть, объясню наконец,  как  дать  ВГ  команду

прочитать, например, группу секторов.  Замечу, что команды чте-

ния/записи  не  раскручивают мотор!, это делают команды позици-

онирования.  Команду можно подавать  только когда микросхема не

занята   выполнением  предыдущейкоманды.


 Позиционирование:

(Объясню  понятия  физической  и логической дорожек: физ. показы-

вает  только положение обеих головок, вне зависимости от сторо-

ны.   Можно   привести  в  соответствие:

номер   !физ:0 0 1 1 ... 80  80

дорожки !лог:0 1 2 3 ... 160 161

Зачем  это надо? дело в том, что ВГ  93  работает с физ. номерами

дорожек, ведь она не знает номер стороны  (см.  порт  #ff),  а мы

привыкли  к  лог.  номеру.  Т.о. лог=физ*2+номер   стороны  (0  -

нижняя)

     di

     ld  a,d ;d - логический но-

             ;мер дорожки

     srl  a  ;  вычисляем физич.

             ; номер (a/2)

     ld  c,#7f; сюда надо помес-

              ;  тить  номер до-

              ; рожки,на которую

              ; я хочу отьехать.

     call outc

     ld   a,#3c; выбор стороны

     bit  0,d

     jr   z,l01

     ld   a,#2c

l01  ld   c,#ff

     call   outc;  устанавливаем

                ;  нужные    для

                ; нормальной ра-

;боты  биты  порта  #ff  и номер

;стороны регистр  #3f устанавли-

;вать не  надо, он должен сам по

;себе  содержать  номер  текущей

;дорожки


     ld a,#18; код команды пози-

;цион.

     call out1

     ld   ix,#3ef5

     call dos; ожидание выполне-

             ;ния

     di      ; команды.

     jp   cont

     ...


out1 ld   c,#1f

outc ld   ix,#2a53

dos  push ix

     jp   #3d2f


 Что делает ВГ 93: при получении в регистр #1f кода #18, она дает

сигнал на раскрутку диска. Затем сравнивает   значения  регистров

#3f  и #7f, определяет направление и количество шагов головки и

шагает,  изменяя  значение  рег. #3f.  При этом значение регистра

#3f  может  и не совпадать с реальным положением головок. Но об

этом поговорим потом. Когда значения  #7f и #3f совпали, ВГ ус-

танавливает bit 7 порта #ff =1 и готова к приёму следующей коман-

ды.

 За  время  позиционирования мотор,  вращающий диск, мог не ус-

петь  раскрутиться  до требуемой скорости  5 об/сек, поэтому тео-

ретически  следует  после выдачи команды позиционирования и перед

чтением/записью   сделать  паузу (паспортное  время раскрутки мо-

тора  0.7  сек.). Задержка также нужна  на время успокоения голо-

вок  после  позиционирования (по паспорту 15 мс). Однако опыт по-

казывает,  что перед чтением задержку  можно  не делать вообще,

перед  записью  же - тут Вам решать,  скажу лишь, что в уже два

года  существующем командере fpm задержка  перед записью делается

только при первом позиционирова нии перед записью первого секто-

ра из всей группы.



 Итак,  ездить  по  диску мы уже умеем  и мотор уже крутится. Те-

перь можно читать сектора:

- даём команду "чтение сектора"

- читаем сектор

- проверяем наличие ошибки затем  повторяем  для  остальных

секторов  на данной дорожке. При переходе  на  следующую  дорожку

надо опять дать команду позицио нирования. Надо сказать, что оп-

ределение   наличия   ошибки  по #2740 нерационально при работе с

группой   секторов.   Существует другой  способ.  Процедура #2090

позволяет прочитать сектор, про верив затем, была ли ошибка. Для

её вызова необходимо:

  поместить в стек:

1) адрес возврата.

2) адрес,указывающий на байт #01

3) bc=#17f

4)  в  (#5cd6)  записать число n (любое)

5) поместить в регистр #5f номер сектора

6) войти в пзу по адресу #2090 В  hl  должен  быть адрес, куда

читать или откуда писать сектор.


cont ld   bc,exit

     push hl

     push bc

     ld bc,cont; адрес, указыва-

               ;ющий на 1

     push bc

     xor  a

     ld   (#5cd6),a

     ld   a,номер сектора

     ld   c,#5f

     call outc

     ld   bc,#2090

     push bc

     ld   bc,#17f

     jp   #3d2f

exit di ; процедура #2090 разре-

        ; шает прерывания

     pop  hl

     ld   a,(#5cd6)


 Здесь a будет равно записанному ранее   в  (#5cd6)  числу,  если

ошибки не было, или на 1 больше, если  была. Если ошибка была, то

следует либо повторить операцию, либо,  если действия Вашей прог-

раммы   зависят   от  конкретной ошибки, то воспользоваться #2740

для определения кода ошибки. Если  ошибок  не  было, то следует

перерассчитать   значения  hl  и 'номера  сектора' для следующего

сектора и повторить операцию.


 Подпрограмма для записи сектора выглядит несколько иначе:

cont ld   bc,exit

     push hl

     push bc

     ld  bc,cont;адрес,указываю-

                ;щий на 1

     push bc

     xor  a

     ld   (#5cd6),a

     ld   a,номер сектора

     ld   bc,#15f

     call outc

     push  bc; в стек надо запи-

             ;сать #01xx

     ld   bc,#2099

     push bc

     ld   bc,#3fca

     push bc

     ld  a,#a0;  команда 'запись

              ;сектора'

     call out1

     ld   bc,#17f

     jp   #3d2f

exit di ; процедура #2090 разре-

        ; шает прерывания

     pop  hl

     ld   a,(#5cd6)


 Дальше аналогично загрузке.


 Какие  могут быть ошибки? (коды из #1f)


При чтении:

 bit  7 =1 дисковод не готов (не крутится мотор.

 bit  5  =1 не обращайте на него внимания.  На  всех 'нормальных'

дисках он =0.

'& bit  4  =1  нет сектора. Обычно что-то где-то не читается.

 bit  3  =1  сектор  читается  с ошибкой.

 bit  0  =1 Вы дали ВГ команду в тот момент, когда она уже выпол-

няла что-то (?).  Вероятность  такой  ошибкипрактически равна нулю.


При записи:

 bit 7 - аналогично чтению.

 bit  6  =1  на дискете заклеена прорезь 'защита записи'.

 bit  5  =1  ошибка  записи.  Не знаю, что сие означает.

 bit 4 и 0 - аналогично чтению.


 При   корректном   обращении  к контроллеру  вероятность появле-

ния ошибок bit 2 и 1 также равна нулю.

Ну вот, на сегодня всё.

Ждите продолжения...

(C) Макс Петров








Системные переменные TR-DOS


    Дисковой  операционной системе  также необходимо  где-то

хранить  информацию о  дисководах и режимах работы, файлах и

т.п.

    Для  этого она  используется область  начиная  с  адреса

#5CB6 (23734)

    Для системных переменных TR-DOS еще не сложились имена в

технической  литературе (точнее  говоря их  вообще никто  не

давал),  поэтому я  здесь наберусь  наглости дать  эти имена

сам.


UNIT_F, байт, #5CB6, X, 23734

    Используется  при наличии  INTERFACE  1.  Если  значение

равно  #F4, то  область остается  на старом месте. Если байт

равен    0,   то   происходит   проверка   байта   #5D18   и

соответствующая операция.


S_RET, байт, #5CC2, , 23746

Содержит  код команды  RET. Используется  TR-DOS для  вызова

подпрограмм Бесйик-ПЗУ.


MODE_A, байт, #5CC8, ,23752

Код режима работы дисковода А.

бит 7=1, если дисковод 80-ти дорожечный.

бит 1=1, если дисковод двухсторонний

бит 0=0, если 80-ти дорожечный дисковод используется как 40

         дорожечных


MODE_B, байт, #5CC9,X, 23753

Код режима работы дисковода В. См MODE_A


MODE_C, байт, #5CCA,X, 23754

Код режима работы дисковода С. См MODE_A


MODE_D, байт, #5CCB,X, 23755

Код режима работы дисковода D. См MODE_A


C_SERD, байт, #5CCC, X, 23756

Текущий сектор при чтении каталога


READY, байт, #5CCD, X, 23757

Содержит #80 при готовности текущего дисковода.


TRMODE, байт, #5CCE,X, 23758

Режим работы:0 чтение/#FF запись


DOS_ER, байт, #5CD6, X, 23766

Содержит #FF, если команда не выполнена


X_BYTE, слово, #5CD7, X, 23767

    Для  файлов типа  BASIC или  BYTE -  адрес старта. Также

содержит число дорожек сразу после тестирования дисковода.


CHADD2, слово, #5CD9, X, 23769

Внутренний  аналог CH_ADD.  Также примежуточная  длина файла

типа BYTES или BASIC


LENPRG, слово, #5CDB, X, 23771

Длина бейсик программы


NAME_F, 8 байт, #5CDD, , 23773

Имя файла


TYPE_F, байт, #5CE5, , 23781

Тип файла


PAR_F, слово, #5CE6, , 23782

Параметры файла:

Для Бейсика - длина бейсик программы без переменных

Для кодов   - стартовый адрес


LEN_F, слово, #5CE8, , 23784

Для файла


SEC_F, байт, #5CEA, , 23786

Размер файла в секторах


FIR_SE, байт, #5CEB, , 23787

Номер первого сектора файла


FIR_TR, байт, #5CEC, , 23788

Номер первой дорожки файла


A_UNIT, байт, #5CEF, X, 23791

Содержит 1 если есть INTERFACE 1


CUR_SEC, байт, #5CF4, X, 23796

    Номер текущего сектора


T_DRV, байт,#5CF6, , 23798

Дисковод  для временной  операции (от 0 до 3). Например LOAD

"A:HELP.ASM" CODE


DOS_ON, слово, #5CF7, , 23799

Обнуляется при возврате из TR-DOS


DBLDRV, байт, #5CF8, , 23800

Дисковод  при  операциях  с  двумя  дисководами,  в

противном случае #FF.


DBLMOD, байт, #5CF9, , 23801

    Режим  работы при  работе с  двумя дисководами  (признак

операции  READ/VERIFY). #FF  - VERIFY,  00 READ.  Сюда также

заноситься номер дискова при внутренней операции 7 (CAT).


TIME_A, байт, #5CFA, , 23802

Время перемещения головки дисковода А.


TIME_B, байт, #5CFB, ,23803

Время перемещения головки дисковода В.


TIME_C, байт, #5CFC, , 23804

Время перемещения головки дисковода С.


TIME_D, байт, #5CFD, ,23805

Время перемещения головки дисковода D.


WG_CODE, байт, #5CFE, X, 23806

Код команды для контроллера дисковода ВГ93


SEC_WRK, байт, #5CFF, X, 23807

Номер сектора для подпрограмм записи, чтения


BUF, слово, #5D00, X, 23808

Текущий адрес буфера


HID_HL,слово,#5D02, X,23810

Временно хранит HL при необходимости


HID_DE, слово, #5D04, X, 23812

Временно хранит DE при необходимости


CH_NAME, байт, #5D06, , 23814

Количество  символов, по  которым осущесвляется  поиск имени

файла.  Нормальное значение - 9, но вы можете его уменьшить.

Увеличивать не рекомендую.


DELF, байт, #5D07, X, 23820

Количество удаленных файлов


DELN,байт,#5D08,X,23816

Первый символ удаленного файла


WRKBUF, слово,#5D0C,X,23820

Флаф  наличия рабочего  буфера TR-DOS  (257  байт  с  адреса

23846):

FF - отсутствует

00 - существует


FL_COM,байт,#5D0E,X,23822

Флаг принадлежности команд бейсика

FF - бейсик,

любое другое значение - TR-DOS


ER_FL,байт,#5D0F,X,23823

Код  ошибки TR-DOS.  При равенстве  0 выводит пустую строку,

иначе пустую строку (подпрограмма по адресу #20EF)


ER_HIGH,байт,#5D10,X,23824

Старший  байт ошибки.  При переходе в TR-DOS обнуляется. При

вызове подпрограмм TR-DOS следует обнулять принудительно.


COMADD,слово,#5D11,X,23825

Адрес строки команды TR-DOS. При вызове:

15616 - хранит E_LINE

15619 - хранит CH_ADD


ERRSP2, слово,#5D13,X,23827

Копия  ERR_SP.При равенстве  старшего байта  АА  выполняется

команда RUN "boot" и в #5D18 заноситься #FE


MES_ON,байт,#5D15,X,23829

При   равенстве  00  печатает  сообщения  TR-DOS,  иначе  не

печатает.


SYSREG,байт,#5D16,,23830

Копия системного регистра (Микросхема 555ТМ9)


X_FL,байт,#5D17,,23831

При  не равенстве  АА рисует  заставку, при  равенстве FF не

попадает на ошибку при чтениее неверного адресного маркера.


INT_ON,байт,#5D18,X,23832

Используется  при подключенном INTERFACE1. Если равно FF, то

меняются  блоки в памяти по адреса 23747 и 23959 длиной в 45

байт. При вызове TR-DOS заноситься FF


DRVDEF,байт,#5D19,,23833

Дисковод по умолчанию (0-3)


RETADD,слово,#5D1C,X,23834

Адрес процедуры завершения. Нормальное значение #0201


HID_SP,слово,#5D1C,X,23836

Временно сохраняет регистр SP


TRLINE,3 байта,#5D20,,23840

Первые три символа введенной строки












Из журнала ZX-Guide#1, Рязань, 28.11.1998


                TR-DOS

            Level 1: 15635

 Alone Coder

    Функции  TR-DOS, вызываемые из Бейсика,

обеспечивают  использование далеко не всех

возможностей системы. Разработчики предус-

мотрели  ряд  специальных функций работы с

дисководом, обращение к которым может про-

исходить  исключительно  из программ в ма-

шинных кодах. Это так называемая <работа с

диском  на  высоком  уровне>, в отличие от

файлового  уровня  и низкого, т.е. прямого

указания контроллеру.

   Все  вышеупомянутые функции выполняются

одной программой,переход к которой осущес-

твляется  по адресу 15635. На самом деле в

ячейке памяти с указанным адресом содержи-

тся  NOP/JR, но есть данные, что переход к

адресу  15636 срабатывает не на всех видах

Speccy. Параметром  для этой программы яв-

ляется регистр C процессора,в котором дол-

жен содержаться номер команды согласно ни-

жеследующего списка:

 

      #5 - чтение секторов.

   Считать  с текущего  дискодава подряд B

секторов по 256 байт и запомнить их по ад-

ресу HL. Начальная дорожка(трек)должна со-

держаться в D,начальный сектор на этой до-

рожке - в E. Обычно эта функция используе-

тся  в загрузчиках моноблочных программ. В

этом случае следует сделать LD DE,(23796),

то  есть  вспомнить номер сектора вслед за

последним считанным/записанным блоком дан-

ных,т.е. в данном случае сектор аккурат за

бейсиковой частью.

   Как  сделать моноблочный загрузчик? пи-

шете сначала вот такую бейсик-программу:

  10 REM 16 штук пробелов

  20 CLEAR 24575:RANDOMIZE USR 23872

Потом с адреса 23872 (если обращения к TR-

DOS  со времени последнего сброса не было,

то с 23760) любым  приятным  вам  способом

заносите кодовую программу:

   LD DE,(23796)

   LD BC,#NN05;где NN=длина кодовой части,

   LD HL,ADDR;а это - адрес загрузки.

   CALL 15635

   JP START;адрес запуска кодовой части.

Она, очевидно,занимает весь REM. Это самый

простой случай - когда кодовый блок один и

запускается без распаковки.В более сложных

случаях  добавьте  пробелов  после  REM  и

вставляйте всё, что вам заблагорассудится.

Запишем нашу бейсиковую прогу на диск:

 RANDOMIZE USR 15619:REM:SAVE"IGRA"LINE 10

За  ней впритык спишем кодовый блок, после

этого загрузим Disk Doctor и исправим дли-

ну бейсик-файла в секторах с 01 на суммар-

ную длину бейсика и кодов,в первом символе

кодового файла запишем код 00,а в 8-м сек-

торе уменьшим на число кодовых файлов байт

номер #E4. Загрузчик готов,и он даже копи-

руется.Конечно,лучше был бы загрузчик типа

   0 INK USR .<а дальше бред>

 

      #6 - запись секторов.

   Записать  B  секторов  подряд по тем же

правилам.Следует заметить,что эти процеду-

ры останавливаются по Break и выдают сооб-

щение Disk error и т.д. с очисткой экрана.

Если вам угодно это предупредить, запишите

предварительно  по адресу 23746 вместо RET

команду вызова обработчика ошибок.

 

      #7 - показ каталога.

   Эквивалентно команде CAT с тем исключе-

нием,что вывод производится в любой поток.

Номер потока задается в A(стандартно - 2).

Желательно записать номер дисковода(23798)

также по адресам(23800)и(23801). Абсолютно

бесполезная команда.

 

      #0 - команда восстановления.

   Головка дисковода возвращается на нуле-

вую дорожку.

 

      #1 - смена дисковода и тестирование.

   В регистре A должен быть номер дисково-

да:0="A",1="B",2="C",3="D". Тестирование -

определение количества дорожек (40 или 80)

и скорости перемещения магнитной головки -

не производится, если дисковод уже был от-

тестирован.

 

   Следует отметить, что перед использова-

нием этих команд нужно открыть область си-

стемных переменных TR-DOS.Сама по себе она

после сброса не открывается, отчего виснут

все известные мне ассемблеры.Можно войти и

сразу же выйти из DOS, можно также вызвать

процедуру 15649.

   Еще неприятной особенностью этих проце-

дур является то,что при чтении/записи сек-

торов  подряд они всякий раз делают лишний

оборот диска при переходе на следующую фи-

зическую дорожку.Этот недостаток исправля-

ют многочисленные TURBO-LOADER'ы,использу-

ющие непосредственное обращение к контрол-

леру дисковода КР1818ВГ93.

   Программа 15635 имеет и другие функции,

но они мало относятся к нашей теме.Коснусь

напоследок только одной:

 

      #13 - копирование дескриптора.

   Копирует 16 байт из адреса HL по адресу

23773 (область дескриптора в системных пе-

ременных TR-DOS). Этой командой можно про-

сматривать  коды  TR-DOS  из программы вне

зависимости  от версии системы, не включая

дисковода.









Из журнала ZX-Guide#2, Рязань, Ноябрь 1999

(в текст внесены исправления из Errata IG#8)


                 TR-DOS

            Level 2: КР1818ВГ93

       Программирование контроллера.

AlCo

    Микросхема ВГ93 - контроллер дисковода.

Она является сердцем интерфейса Beta-Disk,

на основе которого работает всеми нами лю-

бимый TR-DOS;)) (Хотя почему ";)"? Если вы

думаете, что это самая глючная и неудобная

система, то вы и представить себе не може-

те,насколько глючны программы Windows'95 и

Microsoft Office! Одно дело - слышать о их

глюках, и совсем другое - ежедневно  стра-

дать от них...)

   Сей  контроллер в сочетании с Beta disk

интерфейсом  поддерживает запись на диск в

двух форматах: FM и MFM (последний исполь-

зуется  шире) на не более чем 4 дисковода,

содержащие  до 256 дорожек и до двух голо-

вок.Для ламеров нелишне добавить,что обыч-

но используются двухсторонние пятидюймовые

дисководы  с числом  дорожек от 83 до 87 и

скоростью передачи  информации около 30k в

секунду (5 оборотов  в секунду  при  длине

дорожки 6300 байт).

   Микросхема  занимается управлением дви-

гателями  и головками  дисководов, снятием

сигналов с датчиков  на дисководах и пере-

дачей данных туда-сюда по последовательно-

му  порту (не более чем на одном дисководе

сразу).

   Для  всего этого контроллер имеет неко-

торое количество  буферных регистров и до-

статочно сложную систему команд.

   Взаимодействие с процессором компьютера

в интерфейсе  Beta Disk осуществляется че-

рез следующие порты,соответственно отожде-

ствляющиеся с регистрами контроллера:

  31=#1F на запись - Регистр команд

  31=#1F на чтение - Регистр состояний

  63=#3F двунаправленный - Регистр дорожки

  95=#5F двунаправленный - Регистр сектора

  127=#7F двунаправленный - Регистр данных

 

   а также особый порт с адресом #FF, пре-

доставляемый  самим Beta Disk интерфейсом;

Этот  порт называется Системный регистром.

Начнём с него (прямо по книге А.Ларченко и

Н.Родионова "TR-DOS для ..."):

 

   При чтении из системного регистра имеют

смысл  только старшие два разряда, которые

отражайт  состояние сигналов микроконтрол-

лера  DRQ^ (D6) и INTRQ^ (D7). DRQ^ - сиг-

нал,отражающий запрос данных микроконтрол-

лером,INTRQ^ - сигнал окончания выполнения

команды.

   Для записи в системный регистр доступны

пять разрядов.

D0,D1 выбор дисковода.Установив соответст-

  вующий код,можно выбрать один из четырёх

  возможных дисководов:00 для дисковода A,

  01 для B, 10 для C и 11 для D;

D2 аппаратный сброс микроконтроллера...

  (Если  сбросить  этот бит, то выполнение

  текущей команды прекращается, мотор дис-

  ковода глохнет и лампочка гаснет.)

D3 этот разряд блокирует сигнал HLT микро-

  контроллера, для нормальной работы в нём

  должна  быть записана единица; (С другой

  стороны, если  записать туда единицу, то

  вертушка дисковода,заведённая какой-либо

  командой, через  некоторое  время плавно

  остановится. А это чаще всего приводит к

  зависанию программ чтения с диска в слу-

  чае ошибки чтения.Яркий пример:загрузчик

  Magic-файла в TR-DOS.)

D4 выбор магнитной головки.Содержимое это-

  го разряда напрямую транслируется в дис-

  ковод. 0 соответствует  первой магнитной

  головке или нижней  стороне дискеты, 1 -

  второй  магнитной  головке  или  верхней

  стороне;

D6 выбор  плотности  записи. Сброс разряда

  заставляет  микроконтроллер  работать по

  методу модифицированной частотной  моду-

  ляции (MFM),  установка -  по методу ча-

  стотной модуляции (FM). (Есть данные,что

  FM большинством Beta disk'ов не поддер-

  живается.)

 

   Команды  контроллеру  передаются  через

регистр команд (31). Команда (кроме команд

принудительного прерывания) будет выполне-

на, только если контроллер не занят выпол-

нением другой команды.Самый простой способ

записать команду в регистр команд - вызва-

ть процедуру TR-DOS 5.03 с адресом 12227:

       LD A,<команда>

OUT_31 LD IX,12227

DOS    PUSH IX

       JP 15663 ;15664 работает не везде!

   Для  записи  в прочие порты контроллера

следует  использовать подпрограмму с адре-

сом 10835, содержащую OUT (C),A : RET.

 

        1. Команда восстановления.

 

   Магнитная  головка  текущего  дисковода

отъезжает на нулевую дорожку. Код команды:

%0000hvxx, где h=положение головки (0=под-

нять, 1=опустить, причём при h=0 двигатель

дисковода  не заводится), v=режим проверки

номера дорожки (если v=1,то с диска считы-

вается  номер дорожки и сравнивается с со-

держимым регистра дорожки),xx=скорость пе-

ремещения головки (x=00 - максимальная).

   Рекомендуемый код команды: 8.

   Адрес подпрограммы TR-DOS 5.03: 15768

(выход после успешного окончания операции,

либо по BREAK).

 

       2. Принудительное прерывание.

 

   Прерывает  выполнение  текущей операции

при достижении  одного из заданных условий

(условия задаются установкой соответствую-

щих битов в коде команды: %1101jjjj): D0 -

после  перехода  сигнала  CPRDY из низкого

уровня в высокий; D1 - наоборот; D2 - пос-

ле прихода индексного импульса (т.е. обна-

ружено начало дорожки либо просто диск от-

сутствует); D3 - немедленно. Остановка та-

кже произойдёт  немедленно, если  сбросить

все перечисленные биты.

   Рекомендуемый код команды: #D0.

   Адрес подпрограммы TR-DOS 5.03: 12225.

 

        3. Команды позиционирования.

 

   Перемещают головку дисковода. Важной их

чертой является то, что они при этом раск-

ручивают вертушку дисковода (что, впрочем,

делает и команда восстановления).

 

   Шаг к центру: %010ihvxx,где h-положение

головки, xx-скорость,v-проверка,i-будет ли

изменяться  регистр дорожки. Рекомендуемый

код команды: #58.

 

   Шаг от центра: %011ihvxx. Рекомендуемый

код команды: #68.

 

   Шаг в текущем направлении %001ihvxx.Ре-

комендуемый код команды: #38.

 

   Поиск дорожки: %0001hvxx.Переход на за-

данную дорожку.Номер требуемой дорожки ну-

жно поместить в регистр данных(перед запи-

сью команды,иначе не сработает на турбиро-

ванных ВГ).Рекомендуемый код команды: #18.

 

         4. Команды чтения.

 

   Чтение сектора(-ов): %100msec0, где m=0

(иначе будут обрабатываться все сектора до

конца дорожки. При этом номер каждого сле-

дующего  сектора  нужно заносить в регистр

сектора. Соответствующего участка подпрог-

рамм TR-DOS найти не удалось),s=номер сто-

роны (0-нижняя,1-верхняя. Принципиально не

важно,используется для проверки),e=задерж-

ка 15 мс между  установкой головки в рабо-

чее положение и началом операции, c=прове-

рка  указанного  номера стороны с номером,

считанным из заголовка сектора(лучше сбро-

сить, поскольку  как  правило  в заголовке

указана  неверная  сторона). Рекомендуемый

код команды:#80. Адрес подпрограммы TR-DOS

5.03, передающей данные из регистра данных

в память: 16341 (HL=addr,C=#7F,предварите-

льно  нужно записать команду в регистр ко-

манд. Если по выходе из программы B=0, это

указывает на ошибку чтения).

 

   Чтение заголовка сектора: %11000e00,где

e=задержка. Считывается 6 байт  из первого

попавшегося заголовка сектора:

      номер дорожки (0Ў86)

      номер стороны (0)

      номер сектора (1Ў16)

      длина сектора (0,1,2,3 соответствен-

но означает 128,256,512 или 1024 байта)

      2 байта Контрольного Кода.

При выполнении этой команды содержимое ре-

гистра дорожки пересылается в регистр сек-

тора.Рекомендуемый код команды: #C0.

 

   Чтение дорожки: %11100e00,где e=задерж-

ка. С дорожки  считывается вся информация,

включая пробелы, служебные байты и контро-

льные коды. Таким образом,считанная инфор-

мация не годится для команды "запись доро-

жки" (см.ниже).Ещё один маленький трабл: в

процессе выполнения команды происходит по-

теря синхронизации данных,поэтому несколь-

ко  сот байт в конце дорожки остаются неп-

рочитанными(каждый раз разное количество).

Рекомендуемый код операции: #E0.

 

          5. Команды записи.

 

   Запись сектора(-ов): %100mseca,где m=0,

s=0,e=задержка,c=0, a=указывает на один из

двух возможных форматов сектора.В дальней-

шем при считывании этот формат будет инди-

цироваться  в 5 бите  регистра  состояний.

Обычно  этот бит обнуляют, при этом в поле

заголовка  сектора формируется специальный

байт #FB,в противном случае - байт #F8.Ад-

рес  подпрограммы  TR-DOS 5.03, передающей

данные  из  памяти в регистр данных: 16314

(HL=addr, C=#7F,предварительно нужно запи-

сать команду в регистр команд).

 

   Запись(форматирование)дорожки %11110e00

(где e=задержка, которая  здесь совершенно

ни  к чему). Записывает на текущую дорожку

около 6k разнообразной информации, которая

последовательно  передаётся  через регистр

данных. Для  успешного  использования этой

команды  нужно  знать  формат дорожки. Для

примера можно посмотреть подпрограмму фор-

матирования  TR-DOS  по адресу 8189. А для

начала цитата из книжки:

 

   Эта команда предназначена  для разметки

дискеты,то есть для её форматирования. Ин-

формация,посылаемая в микроконтроллер,дол-

жна  полностью  соответствовать выбранному

формату.Запись автоматически начинается по

приходу индексного импульса, то есть с на-

чала дорожки. В отличие от записи сектора,

количество записываемых байтов не фиксиро-

вано, оно определяется конкретным форматом

дискеты. Часть байтов будет просто записы-

ваться,однако несколько из них интерпрети-

руются в этой команде специальным образом.

Они предназначены для формирования служеб-

ных отметок,таких как адресные маркеры или

контрольные коды (КК).

 

байт        FM                 MFM

#F5   Не допускается   Поле A1 (иниц-я КК)

#F6   Не допускается     Синхроимпульс C2

#F7   Зап.двух байт КК    \

#F8ЎFB Инициализация КК   |-обычные байты

#FC   Зап.индексной метки /

#FE   Иниц-я КК        Адр.метка заголовка

 

   Формат дорожки включает в себя несколь-

ко полей, для правильного формирования ко-

торых необходимо придерживаться определён-

ных стандартов...

   Рассмотрим формат дорожки более подроб-

но. Между информационными полями находятся

области пробелов, служащие для синхрониза-

ции  внитренних схем микроконтроллера. Чем

больше поля пробелов,тем лучше синхронизи-

руется контроллер, тем меньше сбоев проис-

ходит  при  передаче информации. В таблице

приведены рекомендуемые значения длин про-

белов, а также значения байта, которым они

заполняются. Уменьшив длину пробелов,можно

получить  некоторый  выигрыш  в  объёме за

счёт потери надёжности.(Величина последне-

го пробела (последнего потому,что он пред-

шествует формированию дорожки)особенно ва-

жна.Небольшая длина,установленная авторами

TR-DOS, не позволяет, кроме всего прочего,

считывать первый сектор дискеты на больши-

нстве IBM-совместимых компьютеров.) (Не на

большинстве,а на всех - это ограничение пц

'шного контроллера;причём это никак не за-

висит от копировщика (AMD, Hobeta и т.п.),

который  вы  используете! Порядочную длину

последнего  пробела проставляют программы:

FUT,ADS,DCU 2.21 и некоторые другие.)

   Каждый сектор логически состоит из двух

информационных полей: поля заголовка и не-

посредственно  следующего за ним поля дан-

ных. В заголовке записана служебная инфор-

мация о секторе,для этого использованы че-

тыре  байта. Номер дорожки и номер сектора

идентифицируют конкретный сектор. Принято,

что  дорожки считаются с нуля, а сектора с

единицы.. (Логический № сектора получается

вычитанием единицы.)

   Каждое информационное поле заканчивает-

ся двумя байтами контрольного кода.

   В качестве  данных  при  форматировании

дорожки можно использовать любой не служе-

бный байт, но обычно  применяют коды 0 или

#FF.

   Порядок  следования секторов на дорожке

может  быть произвольным. Обычно он кратен

некоторому целому числу,называемому inter-

leaving - чередование.Если это число равно

единице, то сектора расположены последова-

тельно: 0,1,2... Для двух сектора распола-

гаются  через  1, для трёх - через 2 и так

далее. Чередование секторов влияет на ско-

рость  обращения  к диску. После окончания

записи  или  считывания очередного сектора

программа  обычно  должна выполнить какие-

либо действия, а на это затрачивается вре-

мя,за которов дискета успевает повернуться

на определённый угол.И к моменту очередно-

го  обращения к диску под магнитной голов-

кой может оказаться сектор, далеко отстоя-

щий от предыдущего.Если секторы расположе-

ны последовательно,то при обращении к сле-

дующему  сектору программе придётся ждать,

пока дискета сделает целый оборот. При со-

ответствующем  чередовании  секторов время

ожидания очередного сектора можно свести к

минимуму. (Оптимальный интерлив, обеспечи-

вающий  наибольшую скорость через операции

TR-DOS - это  единица (сектора расположены

последовательно). Но  не стоит думать, что

это  единственно  возможный  или наилучший

вариант. Например, загрузчики с музыкой во

многих демонстрациях рассчитаны под интер-

лив, равный 2 (как раз так форматирует TR-

DOS),а на видеодиске LOCOMOTION (Speed Co)

сектора  расположены вообще в обратном по-

рядке ("пессимальный" интерлив).Существует

также константа межтрекового интерлива: на

сколько секторов сдвигается последователь-

ность  секторов от дорожки к дорожке (речь

идёт о физических  дорожках, которых є87).

Обычно трековый интерлив равен нулю, но на

дисководах Teac, благодаря  встроенному  в

них искусственному интеллекту, для быстрой

работы  турбо-лоадеров  необходим трековый

интерлив  не меньше двойки. А для подпрог-

рамм TR-DOS (15635=#3D13) оптимальное зна-

чение - около 5 (так форматирует CONSUL).)

 

         Формат дорожки для MFM.

 

Последний пробел:   80x#4E

                   ~12x0

Поле C2:            3x#F6 \

Индексная метка:    1x#FC |-необязательно

Первый пробел:     ~50x#4E|

                   ~12x0  /

 Поле A1:            3x#F5

 Адр.метка заголовка:1x#FE

 Номер дорожки

 Номер стороны

 Номер сектора

 Длина сектора

 Формирование КК:    1x#F7

 Второй пробел:     ~22x#4E (не меньше 12)

                    ~12x0 (не меньше 6)

 Поле A1:            3x#F5

 Адр.метка данных:   1x#FB

 Данные              NNxNN

 Формирование КК:    1x#F7

 Третий пробел:     ~54x#4E (не меньше 12)

Четвёртый пробел:   NNx#4E

 

         Регистр состояний #1F.

 

D7 - готовность дисковода

D6 - защита  записи (устанавливается всеми

   командами, кроме команд чтения)

D5 - в командах записи - индицирует ошибку

   записи (честно скажу, штука крайне ред-

   кая); в команде чтения сектора - повто-

   ряет значение бита a (см. запись секто-

   ров)

D4 - ошибка позиционирования (если исполь-

   зовалась проверка) или сектор не найден

D3 - ошибка в контрольном коде (либо  диск

   защищён, либо 1-2 бита прочитались неу-

   дачно)

D2 - потеря данных

D1 - в командах позиционирования - индекс-

   ный импульс. В других командах - сигнал

   на запрос данных  либо указание принять

   байт из регистра данных

D0 - занято, идёт выполнение команды.

   Считать его состояние можно несколькими

обходными  способами (специальной подпрог-

раммы  чтения  портов в TR-DOS 5.03 нет, и

если вы нашли её у себя, значит, у вас не-

стандартная версия ПЗУ).

   Способ Н.Родионова:

- Записать 0 в регистр дорожки;

- Записать #0A в регистр сектора;

- Записать 1 в регистр D;

- Вызвать подпрограмму по адресу 16179;

- Взять из рег. B значение рег. состояний;

- Восстановить в регистрах дорожки  и сек-

тора исходные значения.

   Способ М.Петрова:

- Записать в D номер дорожки;

- Записать в (#5CD8) число, отличное от 0;

- Вызвать подпрограмму #2740;

- Прочитать из (#5CCD) регистр состояний.

 

   В общем, данные  сугубо энциклопедичес-

кие. Но продвинутый читатель поймёт. Неко-

торые  детали  иллюстрирует исходник прог-

раммы MonoLoad.H, помещённый в Барахло.

   Подпрограммы TR-DOS проще всего  иссле-

довать в STS 5.1 (только не 6.2), выбрав в

Setup:ROM=DOS. Только учтите глюк в STS: в

DOS неверно трассируются команды LD r,(HL)

и им подобные.

 

              Литература.

1. А.Ларченко,Н.Родионов. TR-DOS для поль-

зователей и программистов(Питер)

2. Макс Петров о TR-DOS'е(ZX-Format#5)

3. PAUL ATRIDES."doc_help"(Spectrophoby#6)







Из журнала ZX-Guide#3, Рязань, 03.12.2000

             TR-DOS: Level 3.

Basil & AlCo

------------------------------------------

        #3D13 с обработкой ошибок.

AlCo

   На  этот  раз я поведаю вам о некоторых

тонкостях  TR-DOS, связанных  с написанием

системных  программ (в  которых  вроде как

обязательно  должна использоваться станда-

ртная точка входа TR-DOS по адресу #3D13).

А Basil  напишет  о тонкостях, связанных с

написанием boot'ов.

 

           1. Смена дисководов.

    Следует ориентироваться  на версию 5.03

как  на низшую из распространённых (сейчас

у меня она прошита).

   Способ таков: записываем во все систем-

ные переменные, связанные с номером диско-

вода, нужное  нам значение, и обнуляем для

всех дисководов параметр "время перемещения

головки"(в TR-DOS 5.03 этот параметр поче-

му-то  не  инициализируется). Способ также

хорош  тем, что  позволяет использовать 26

дисководов (от A до Z).Только я ещё не ви-

дел версию TR-DOS, которая бы поддерживала

более 4 дисководов...

   Короче,вот к чему я клоню (это подпрог-

рамма из AC Edit v0.48):

 

          2. Собственно драйвер

        #3D13 с обработкой ошибок.

 

SYSBUF  EQU #6000      ;килобайтный буфер

 

INIT    ...    ;должно быть В САМОМ НАЧАЛЕ

        LD HL,ONERROR  ;?

        LD (23747),HL  ;? по адресу 23746

        ...             ?стояло RET, а мы

                        ?ставим JP ONERROR

EM15635 LD A,195       ;?(чтобы перехваты-

        LD (23746),A   ;? вать ошибки)

        LD (EMSP+1),SP

        PUSH BC

        PUSH DE

        PUSH HL

DRIVE   LD A,"A"

        SUB 65

        LD (23833),A ;"д-вод по умолчанию"

        LD (23798),A ;"д-вод для временной

                     ;операции"

        OR #3C

        LD (23830),A ;"копия системного

                     ;регистра"

        LD HL,#5C00  ;придётся запомнить

        LD DE,SYSBUF ;сис. переменные в

        LD BC,#400   ;буфере, иначе после

                     ;No Disk будет облом

        DI

        LDIR

        LD H,L

        LD (23823),HL ;"код ошибки TR-DOS"

                   ;- рекомендуют обнулять

        LD (23802),HL ;"время перемещения

        LD (23804),HL ;головок"

        POP HL

        POP DE

        POP BC

        LD SP,#5F00

        CALL 15635

EMSP    LD SP,0

EMQ     LD HL,HL4

        LD (23746),HL ;снова ставится RET

        RET

ONERROR

HL4     EQU $*256+201

        LD (ERRDE+1),DE

        EX (SP),HL   ;что это такое хотел

        LD DE,8020   ;вызвать TR-DOS?

        OR A

        SBC HL,DE

        JR NZ,ERRSP

        POP HL       ;если это вызывалась

ERRDE   LD DE,0      ;проверка на BREAK,

        SCF          ;то возвращаем ему,

        RET          ;что BREAK не нажат

 

ERRSP   LD SP,0      ;это уже не BREAK,

        LD HL,SYSBUF ;а кое-что похлеще...

        LD DE,#5C00

        LD BC,#400

        DI

        LDIR

        EI

        CALL DEPKFNT ;производятся всякие

        LD HL,SAVERR ;действия,связанные с

        CALL WINDTXT ;обработкой ошибок

        CALL IYPWINQ ;(вывод окошка

        CALL EDUPAGE ;с ошибкой

        OR A         ;и т.д.)

        RET

 

   Пользуются ею так же,как и обычным CALL

15635.Только в (ERRSP+1) перед вызовом ну-

жно записать регистр SP.Или,возможно,SP+2.

Или SP-2. Зависит  от уровня вложенности и

от того,куда вы хотите попасть после обра-

ботки ошибки.

 

           3. Командная строка.

    В журнале Adventurer #5 писали, что ко-

мандную строку TR-DOS нельзя использовать,

поскольку она затирается...

   TR-DOS  при запуске BASIC-файла стирает

первые 7 символов командной строки и заме-

няет их на 0D,80,00,00,40,5D,00.(При запу-

ске  кодовых файлов она не затирается сов-

сем). Для  использования в программах я бы

рекомендовал  следующий  формат  командной

строки:

   RUN "name",param

   После  имени  файла (кстати, не менее 3

букв) следует запятая - потому что так бу-

дет  больше похоже на формат остальных ко-

манд TR-DOS, и,к тому же,системе наплевать

на всё то,что написано после запятой. Опыт

использования  командной строки без разде-

лителей  был  произведён  Бобом Клубовым в

программе "VIEW ZXW" (1996 г, приложение к

журналу ZX-News #3). Это не есть рулез,так

как, встретив  после  имени  файла один из

следующих символов:

 ", #, -, +, *, /, (, <, =, >, CODE, DATA,

TR-DOS  отказывается  исполнять  командную

строку.

   Впервые командная строка использовалась

вроде бы в Z00LOOK'овской версии ECHOLOGY.

То,что сделано в INSULT,трудно назвать оп-

росом командной строки.

 

------------------------------------------

              Пишем boot...

 Basil

          Как включить 48-ой Basic

            со 128-ой памятью?

 

   Это по правде делается очень просто:

        DI

        LD IY,23610

        LD HL,4867

        PUSH HL

        LD (23613),SP

        LD HL,(23631)

        LD DE,15

        ADD HL,DE

        LD DE,5566

        EX DE,HL

        LD BC,4

        LDIR

        RES 4,(IY+1)

        LD HL,7030

        PUSH HL

        IM 1

        EI

        LD HL,10072

        EXX

        RET

 

Теперь смело можете выходить в Basic (род-

ной - 48-ой) и радоваться 128-ой памяти.

  

   Выход в 128-меню Basic'а:

         XOR A

        LD BC,32765

        OUT (C),A

        RST 0

 

   Очистка 48-ой памяти и запуск boot'а:

        LD HL,71

        PUSH HL

        LD H,L

        JP 15663

 

          или

        LD HL,0

        PUSH HL

        JP 15663

 

   А теперь уникальная  процедура, которые

многие ищут,теряют,находят вновь...да нет,

ребята, я  не  про любовь, а про процедуру

запуска Basic файлов (для 48-128кб) из ма-

шинных кодов:

 

        ORG #XXXX

        LD HL,#YYYY   ;16 BYTES INFO

        LD DE,23773   ;ABOUT

        LD BC,16      ;BASIC FILE.

        LDIR

        LD A,#C9

        LD (23746),A

        LD HL,RNBASIC

        PUSH HL

        LD DE,23448

        LD BC,END_COD-RNBASIC

        LDIR

        LD HL,10072   ;ДЛЯ

        EXX           ;ВЫХОДА

        LD IY,23610   ;  В

        IM 1          ;BASIC.

        EI

        RET

RNBASIC

       DISP 23448

        LD HL,23867

        LD DE,23868

        LD (HL),0

        LD BC,41668

        LDIR          ;ОЧИСТКА 48-Й ПАМЯТИ

 

        LD A,#38      ;PAPER 7

        LD (23693),A  ;& INK 0.

        LD (23624),A  ;И ДВЕ НИЖНИЕ СТРОКИ

                      ;ЭКРАНА ТАКИЕ ЖЕ.

 

        LD BC,65167   ;CLEAR 65167 ДЛЯ

        CALL 7863     ;МАКСИМАЛЬНО ДЛИННЫХ

                      ;ФАЙЛОВ.

 

        LD HL,4867    ;ПОДКЛЮЧЕНИЕ

        PUSH HL       ;

        LD (23613),SP ;

        LD HL,(23631) ;48-ГО

        LD DE,15      ;

        ADD HL,DE     ;

        LD DE,5566    ;BASIC'А

        EX DE,HL      ;

        LD BC,4       ;

        LDIR          ;СО 128-ОЙ

        RES 4,(IY+1)  ;

        LD HL,7030    ;

        PUSH HL       ;ПАМЯТЬЮ.

 

        LD HL,RNBAS2

        PUSH HL

        LD HL,10608   ;

        PUSH HL       ;

        LD HL,10528   ;

        PUSH HL       ;ЗАГРУЗКА.

        LD HL,10570   ;

        PUSH HL       ;

        JP 15663      ;

 

RNBAS2  LD HL,(23649)

        LD A,(HL)

        DEC HL

        LD L,(HL)

        LD H,A

        LD (23618),HL ;СТРОКА АВТОСТАРТА

        XOR A         ;ВЫПОЛНЯЕТСЯ С

        LD (23620),A  ;1-ОЙ КОМАНДЫ.

        RET

       ENT

END_COD

 

P.S. Эта процедура  сама загрузит и запус-

тит  Basic  файл. Для  ускорения  процесса

нужно  перед  запуском этой проги передви-

нуть головку на трек  запускаемого Basic'а

(через #3D2F).

P.P.S. Эта  процедура  запустит любую 48-ю

прогу, кроме той,которая проверяет наличие

страниц  памяти  или защелку порта #7FFD и

назло не запускается,или той,которая,непо-

нятно для чего, использует порт #FD.

 

   Проверка диска в дисководе:

 

DISK_CP LD A,#C3

        LD (23746),A

        LD HL,DSK_CP2

        LD (23747),HL

        LD HL,#2FC1

        CALL DOS

        LD A,8

        LD L,#C3

        CALL DOS

        LD (STK),SP

        LD D,0

        LD HL,#2740

        CALL DOS

DSK_CP2 LD SP,0

STK     EQU $-2

        LD HL,#1FF3

        CALL DOS

        LD L,#EB

        CALL DOS

        LD HL,#2FBC

        CALL DOS

        LD A,(23757)

        AND 64

        LD (VARPROT),A

        RET

DOS     PUSH HL

        JP #3D2F

 

   Объясняю: вообще-то  проверка  диска  в

дисководе - это проверка на наличие защиты

у диска. (Ред: можно  комбинировать с про-

веркой индексного отверстия)

   Проверка для незащищенного диска проис-

ходит так:

 1) Диск в дисководе - защита отсутствует;

 2) Диск вынимается (индикатор определяет)

- диск защищен;

 3) Диск вынут - диск не защищен;

 4) Диск вставляется - диск защищен;

 5) Диск вставлен - диск не защищен.

   У защищенного так:

 1) Диск в дисководе и диск вынимается -

определяется как одна фаза: диск защищен;

 2) Диск вынут - диск не защищен;

 3) Диск вставляется и диск вставлен -

определяется как одна фаза: диск защищен.

   Сразу, как  только  загрузилась с диска

ваша прога  с этой проверкой, надо опреде-

лить - сколько фаз требуется для этого ди-

ска (диск  может быть защищенным или неза-

щищенным).

   Вообще-то, вы, наверное, и  сами видели

программы с некорректной проверкой диска в

дисководе. По  правде  сказать, здесь есть

только (только!!) два правильных варианта:

 1) Считывание  каталога с диска сразу при

первом  же изменении для защищенного и при

втором для незащищенного диска (чтобы счи-

тывание  у обоих дисков происходило одина-

ково - когда  диск полностью отсутствует в

дисководе);

 2) Считывание каталога при втором измене-

нии для  защищенного  или  при третьем для

незащищенного  диска (каталог считывается,

как только начинают вставлять новый диск).

 

               Вариант 1:

  ( вариант 2 - то же самое, только вместо

"LD A,2" должно стоять "LD A,3" )

 

INIT    CALL READING CAT.

        CALL DISK_CP

        LD A,(VARPROT)   ;В СЛУЧАЕ ЗАЩИТЫ.

        LD (VARPROT+2),A

        AND A

        LD A,2

        JR Z,NO_PROT     ;Z - НЕ ЗАЩИЩЕН.

        DEC A

NO_PROT LD (COLFAZ),A    ;КОЛ-ВО ФАЗ.

 

ZAMOK   CALL DISK_CP

VARPROT EQU $+1

        LD A,0

        CP 0

        JR NZ,DALEE

        LD (VARPROT+2),A

COLFAZ  EQU $+1

        LD A,0

        DEC A

        LD (COLFAZ),A

        JR NZ,DALEE

        JP INIT

DALEE    Это может быть опрос клавиш или

что-то еще, в конце переходит на "ZAMOK"

        ...

        ...

        ...

        JP ZAMOK

 

    TurboMonoLoader & Loader with Int.

 

        ORG 25000

        DI

        LD B,160

        LD HL,26000

R_FROM  LD DE,0

NXT_TRA LD C,D

        LD A,(23833)

        AND 3

        OR #2C

        SRL C

        JR C,$+4

        OR 16

        LD IX,8179 ;OUT (#FF),A

        CALL DOS

        LD A,C

        LD C,#7F

        LD IX,10835 ;OUT (C),A

        CALL DOS

        LD A,#18

        LD C,#1F

        CALL DOS

        LD A,E

        INC A

        LD C,#5F

        CALL DOS

        PUSH BC

        PUSH DE

READSEC PUSH HL

        LD A,#80

        LD C,#1F

        LD IX,10835 ;OUT (C),A

        CALL DOS

        LD BC,#017F

        LD IX,16343

        CALL DOS

        POP HL

        DJNZ READSEC

        POP DE

        INC H

        LD A,E

        INC A

        AND #0F

        LD E,A

        JR NZ,$+3

        INC D

        POP BC

        DJNZ NXT_TRA

        LD (R_FROM+1),DE

        RET

DOS     PUSH IX

        JP 15663

 

  В данном варианте  этот loader будет ра-

ботать как turbo, если диск форматирован с

нужным  для  вашего дисковода смещением (у

Teac - 02, у Robotron - 00, а у вашего ти-

рана может быть до 04),а прерывания запре-

щены.Но если разрешить прерывания - ничего

страшного,эта прога будет и с ними коррек-

тно работать,как и процедуры по прерывани-

ям, только, правда,турбы вы не увидите,тем

более, чем больше тактов занимает прерыва-

ние, тем медленнее считывание.

P.S. Если этой проге  встретился ERROR'ис-

тый сектор,то она будет его считывать,пока

не считает. Если считать невозможно, то ее

можно  остановить сбросом компьютера с ка-

кой-нибудь вышки или отключением  электри-

чества в вашем районе.

------------------------------------------













Иван Рощин

Дополненная версия. Дата последнего редактирования: 10.11.2002.


В TR-DOS каждому файлу ставится в соответствие 16-байтовый описатель, 

находящийся в каталоге диска. Как видно из табл. 1, в этом описателе 

для расширения файла предусмотрен лишь один символ.


Табл. 1 

Смещение от начала Длина Комментарий 

0 8 Имя файла 

8 1 Расширение файла 

9 2 Стартовый адрес файла

(для BASIC — длина программы и переменных) 

11 2 Длина файла в байтах

(для BASIC — длина программы без учёта длины переменных) 

13 1 Длина файла в секторах 

14 1 Номер сектора начала файла 

15 1 Номер дорожки начала файла 



Но в последнее время всё шире используется более информативное 

трёхсимвольное расширение файла (как в MS-DOS):


Табл. 2 

Смещение от начала Длина Комментарий 

0 8 Имя файла 

8 3 Расширение файла 

11 2 Длина файла в байтах 

13 1 Длина файла в секторах 

14 1 Номер сектора начала файла 

15 1 Номер дорожки начала файла 


Видно, что длина расширения увеличилась за счёт использования 

двух байтов, в которых раньше хранился стартовый адрес файла.


Если вы пишете какую-либо программу для работы с файлами, 

неплохо было бы предусмотреть в ней вывод трёхсимвольных расширений. 

Но тут появляется проблема: заранее не известно, с какими файлами 

придётся иметь дело, а расширение у файлов может быть как 

трёхсимвольным, так и односимвольным. Таким образом, для 

каждого конкретного файла ваша программа должна правильно 

определить длину расширения. Если же считать расширение 

всегда трёхсимвольным, то каталог диска будет выглядеть 

неаккуратно:

boot     B±

ZX_WIN   ZIP

******** ZIP

S_PLAY2d BБ

BACKUM   GTR

BUZZ16_1 MPS

FL_SH_EI MPS

KL_F_CUT m!ћ

SPY      MPS

VIVID    MEљ

NOSTALGY Y ђ

s_play2d txt


Попробуем сделать так: определим, какие последовательности 

из трёх байтов являются допустимыми трёхсимвольными 

расширениями, и если в описателе файла окажется 

недопустимая последовательность, то будем считать, 

что у этого файла односимвольное расширение.



Сначала перечислим список требований, 

которым должно удовлетворять трёхсимвольное расширение:

Все три символа — коды в диапазоне #20..#7F. 

В расширении не может быть одновременно прописных 

и строчных латинских букв. 

Несмотря на пункт 2, расширения XAS, xAS, XaS, xaS 

являются допустимыми 

(файлы с такими расширениями создаёт ассемблер XAS). 

Если первый символ — «B», то расширение не может быть трёхсимвольным, 

потому что у BASIC-файлов следующие два байта содержат 

длину программы и переменных и не могут являться 

символами расширения. 

Теперь рассмотрим процедуру, определяющую длину расширения 

файла в соответствии с этими требованиями.


Процедура рассчитана на компиляцию в 

ассемблере ZX ASM 3.10, и при компиляции в другом 

ассемблере вам может потребоваться заменить 

команду «PUSH HL,DE,BC» на три команды: 

«PUSH HL», «PUSH DE» и «PUSH BC». Соответственно, 

команду «POP BC,DE,HL» тоже нужно будет заменить 

на три команды: «POP BC», «POP DE» и «POP HL».


Входные параметры: HL указывает на первый символ 

расширения. Регистр H не должен быть равен нулю.


Выходные параметры: регистры остаются без изменений 

(кроме аккумулятора), а флаг Z несёт информацию о 

длине расширения: если флаг установлен — расширение

 трёхсимвольное; если сброшен — односимвольное.


Длина процедуры всего семьдесят два байта, она 

максимально оптимизирована, и, по-видимому, сделать 

её короче без ущерба для функциональности не получится.


Текст процедуры:

ANALYS_EXT PUSH HL,DE,BC

;Проверка на BASIC:

           LD   A,(HL)

           CP   "B"

           JR   Z,ANALYS_NO


;Проверка на XAS, xAS, XaS, xaS:

           RES  5,A

           CP   "X"

           JR   NZ,ANALYS_EX1

           INC  HL

           LD   A,(HL)

           RES  5,A

           CP   "A"

           JR   NZ,ANALYS_EX2

           INC  HL

           LD   A,(HL)

           CP   "S"

           JR   Z,ANALYS_YES

           DEC  HL

ANALYS_EX2 DEC  HL


;Изначально D=#FF, E=0. Как только

;встречаем символ A..Z или a..z,

;добавляем его к D по AND и к E по OR.

;В пятом бите 0, если буква прописная,

;и 1, если буква строчная. Допустимы

;лишь варианты 0,0,0 и 1,1,1. Если

;вариант 0,0,0 (все буквы прописные), то

;5-й бит D = 0 и 5-й бит E = 0. Если

;вариант 1,1,1 (все буквы строчные), то

;5-й бит D = 1 и 5-й бит E = 1. Для

;других вариантов (когда есть и 0, и 1)

;5-й бит D = 0 и 5-й бит E = 1. Видим,

;что для вариантов 0,0,0 и 1,1,1 5-й бит

;от D XOR E будет равен 0, а для других

;вариантов он будет равен 1, таким

;образом, можно отличать допустимые

;варианты от недопустимых.


ANALYS_EX1 LD   DE,#FF00

           LD   BC,3*256+%00100000


ANALYS_EX3 LD   A,(HL)

           CP   C           ;=CP #20

           JR   C,ANALYS_NO

           CP   #80

           JR   NC,ANALYS_NO;не #20..#7F

           OR   C

           CP   "a"

           JR   C,ANALYS_EX4

           CP   "z"+1

           JR   NC,ANALYS_EX4

           LD   A,(HL)

           AND  D

           LD   D,A

           LD   A,(HL)

           OR   E

           LD   E,A

ANALYS_EX4 INC  HL

           DJNZ ANALYS_EX3

           LD   A,D

           XOR  E

           AND  C

           JR   Z,ANALYS_YES

ANALYS_NO  OR   H  ;Сбрасываем флаг Z

                   ;(т.к. H<>0, то и

                   ;результат операции

                   ;OR будет <> 0).


;При передаче управления на ANALYS_YES

;по JR - флаг Z всегда будет установлен!


ANALYS_YES POP  BC,DE,HL

           RET


Вот, например, как выглядит каталог диска, 

где для каждого файла длина расширения 

определялась с помощью этой процедуры:

boot     B

ZX_WIN   ZIP

******** ZIP

S_PLAY2d B

BACKUM   GTR

BUZZ16_1 MPS

FL_SH_EI MPS

KL_F_CUT m

SPY      MPS

VIVID    M

NOSTALGY Y

s_play2d txt



Теперь хотелось бы сказать несколько слов об имени диска. 

В TR-DOS под имя диска отводится восемь символов, а само 

имя расположено по смещению #F5 в служебном (девятом) 

секторе нулевой дорожки.


Как видим, в этом секторе остаются свободными ещё три 

байта, а значит, имя диска может быть увеличено до 

одиннадцати символов. Действительно, 11-символьное 

имя довольно часто используют. Но тут есть один нюанс…


Если вы хотите присвоить диску 11-символьное имя, то 

старший бит последнего байта обязательно должен быть 

установлен. Дело в том, что подпрограмма печати 

строки в TR-DOS (которая используется для вывода 

имени диска при выполнении команды CAT) заканчивает 

печатать строку или при встрече нулевого байта, или 

когда старший бит последнего напечатанного символа 

был установлен (при печати символа старший бит сбрасывается). 

Если печатается обычное имя из восьми символов, всё 

проходит нормально, так как за ним следуют три 

нулевых байта, а вот при печати 11-символьного имени, 

если старший бит последнего байта не установлен, 

то процедура не остановится, напечатав имя, 

а будет печатать дальше и дальше, заполняя экран мусором.


Соответственно, если вы пишете программу для работы 

с диском и хотите поддержать 11-символьное имя диска, 

то при печати последнего символа имени 

ваша процедура печати должна сбрасывать его старший бит.


Дополнение

Как выяснилось, одно из требований к трёхсимвольному расширению 

(ранее сформулированное так: все три символа — коды в диапазоне #20..#7F) 

нуждается в некотором уточнении.


Во-первых, символ с кодом 96 (обратная кавычка) 

не является допустимым и, следовательно, 

должен быть исключён из данного диапазона.


Во-вторых (об этом мне сообщил Max Vasilyev), 

трёхсимвольное расширение не должно начинаться с пробела 

(например, « ab») или содержать вторым символом пробел («a b»). 

Иначе говоря, пробелом может быть лишь последний символ расширения.


Соответственно, процедура определения длины 

расширения файла теперь должна выглядеть так:


ANALYS_EXT PUSH HL,DE,BC

           LD   BC,3*256+%00100000


;Проверка на BASIC:

           LD   A,(HL)

           CP   "B"

           JR   Z,ANALYS_NO


;Проверка: первый или второй символ - пробел?

           INC  HL

           LD   A,(HL)

           CP   C            ;=CP #20

           JR   Z,ANALYS_NO

           DEC  HL

           LD   A,(HL)

           CP   C            ;=CP #20

           JR   Z,ANALYS_NO


;Проверка на XAS, xAS, XaS, xaS:

           RES  5,A

           CP   "X"

           JR   NZ,ANALYS_EX1

           INC  HL

           LD   A,(HL)

           RES  5,A

           CP   "A"

           JR   NZ,ANALYS_EX2

           INC  HL

           LD   A,(HL)

           CP   "S"

           JR   Z,ANALYS_YES

           DEC  HL

ANALYS_EX2 DEC  HL


ANALYS_EX1 LD   DE,#FF00


ANALYS_EX3 LD   A,(HL)

           CP   C            ;=CP #20

           JR   C,ANALYS_NO

           CP   #80

           JR   NC,ANALYS_NO ;не #20..#7F

           CP   96

           JR   Z,ANALYS_NO  ;"`"

           OR   C

           CP   "a"

           JR   C,ANALYS_EX4

           CP   "z"+1

           JR   NC,ANALYS_EX4

           LD   A,(HL)

           AND  D

           LD   D,A

           LD   A,(HL)

           OR   E

           LD   E,A

ANALYS_EX4 INC  HL

           DJNZ ANALYS_EX3

           LD   A,D

           XOR  E

           AND  C

           JR   Z,ANALYS_YES

ANALYS_NO  OR   H

ANALYS_YES POP  BC,DE,HL

           RET


В регистровой паре HL, напомню, должен задаваться 

адрес первого символа расширения (H<>0!), 

а результат работы процедуры возвращается во флаге Z 

(установлен — расширение трёхсимвольное, сброшен — односимвольное).














(C) Иван Рощин, Москва

TR-DOS: как не допустить ошибки?

(«ZX-Ревю» 5-6/1997)

Дата последнего редактирования: 12.11.2002. 

    Вы удивитесь, если узнаете, сколько программ не 

распознают ошибки при работе с диском, неправильно 

распознают их, а также зависают или сбрасываются 

при их возникновении. Разумеется, при написании 

собственных программ таких ситуаций следует избегать, 

хотя это не так-то просто. Дело в том, что корректная

 обработка ошибок становится возможной лишь при работе 

напрямую с микроконтроллером дискового интерфейса, 

а это доступно далеко не каждому. Несмотря на это, 

надёжность программ можно значительно увеличить, 

если использовать наряду с обычными функциями TR-DOS 

специальные процедуры для распознавания ошибочных 

ситуаций.


    В этой статье пойдёт речь об одной из самых 

распространённых ошибок ввода-вывода — отсутствии 

диска в дисководе. Также будет сказано несколько 

слов о прямом программировании ВГ93 и о программах, 

контролирующих смену диска.


    Известно, что перед началом любой дисковой 

операции, связанной с чтением или записью данных, 

необходимо, чтобы диск был установлен в дисковод 

и дверца дисковода была закрыта. Если это условие 

не выполняется, программа (если она грамотно написана) 

обычно выдаёт одно из следующих сообщений:


No disk. 

Disk not present. 

Disk not ready. 

    Специальная процедура для определения наличия 

диска в дисководе была бы очень полезной. Приведу 

лишь один из возможных примеров её использования: 

если ваша программа использует интерпретатор 

системных функций TR-DOS и не осуществляет 

перехват ошибок, то при их возникновении 

(в частности, при отсутствии диска) может 

произойти сброс компьютера (что очень нежелательно). 

Но если с помощью указанной процедуры обнаружить 

отсутствие диска ещё до вызова интерпретатора, можно 

избежать неприятных последствий 

(конечно, если не произойдёт какой-нибудь другой ошибки).


    Рассмотрим несколько возможных способов 

реализации такой процедуры:


Пробуем прочитать какой-нибудь сектор 

(естественно, не с помощью вызова интерпретатора системных функций, 

а при прямом программировании контроллера). 

Так как считанная информация не понадобится, чтение можно 

проводить, скажем, в область ПЗУ. 

В случае неудачи считаем, что диска нет. 

    Недостатки: неудачное считывание сектора ещё не значит, 

что диск отсутствует. Вполне может быть, что сектор 

был записан с ошибкой, или сектора с таким номером 

вообще нет на дорожке (т.к. в общем случае формат 

диска может быть произвольным).

    Достоинства: при работе с диском 

фиксированного формата этот способ вполне подходит.


Делаем попытку прочитать первый встреченный 

заголовок сектора. 

В случае неудачи считаем, что диска нет. 

    Недостатки: в общем случае диск может 

быть и неформатированным, при этом ничего 

прочитать не удастся, и мы получим неверный результат.

    Достоинства: этот способ годится для 

любого форматированного диска и достаточно 

прост в реализации.


Используем регистр состояния микроконтроллера. 

    Прежде чем говорить о достоинствах и недостатках 

этого метода, вспомним, что такое регистр состояния и 

что можно узнать с его помощью.


    Регистр состояния отражает корректность заданной 

команды, а также состояние микроконтроллера при её выполнении. 

Каждый бит в нём указывает на определённый параметр и связан 

с выполнением конкретной команды. При выполнении команд 

восстановления и позиционирования биты регистра состояния 

имеют следующее назначение:


  0 — занято, идёт выполнение команды.

  1 — индексный импульс.

  2 — магнитная головка находится в исходном положении.

  3 — ошибка в контрольном коде.

  4 — ошибка позиционирования.

  5 — магнитная головка находится в рабочем положении.

  6 — защита записи.

  7 — указывает на готовность дисковода к выполнению команды.


    Нас будут интересовать лишь первый и шестой биты 

регистра состояния, поэтому поговорим о них подробнее.


    Если посмотреть на пятидюймовую дискету, можно увидеть 

большое отверстие в центре, а рядом с ним — маленькое круглое 

отверстие в корпусе дискеты и отверстие чуть меньшего 

диаметра на самом магнитном диске. Это так называемое 

индексное отверстие, которое служит для ориентации магнитной 

головки дисковода на дискете. Когда отверстия в корпусе и 

на самом диске совпадают (а это происходит при каждом обороте диска), 

контроллер считает, что магнитная головка находится в начале дорожки.


    Значение первого бита регистра состояния 

определяется состоянием индексного отверстия:


Табл. 1 Ситуация Значение первого бита 

Диска нет. 1 

Диск есть, отверстия в корпусе и на самом диске совпадают. 1 

Диск есть, отверстия в корпусе и на самом диске не совпадают. 0 


    Шестой бит регистра состояния отражает 

состояние прорези для защиты записи:


Табл. 2 Ситуация Значение шестого бита 

Диска нет. 0 

Диск есть, прорезь открыта. 0 

Диск есть, прорезь закрыта. 1 


    Ясно, что если первый бит равен 0 или шестой бит равен 1, 

диск присутствует. 

Но, во-первых, при наличии диска эти биты могут принимать, 

вообще говоря, любые значения, а во-вторых, надо ещё узнать, 

закрыта ли дверца дисковода. Как же определить её состояние? 

Если она закрыта и двигатель дисковода включен, 

то диск будет вращаться. 

Если же дверца открыта, диск не будет вращаться. 

Ну а как определить, вращается диск или нет, мы уже знаем: 

нужно циклически проверять значение первого 

бита регистра состояния. 

При каждом обороте диска этот бит меняет своё значение с 0 

(отверстие на корпусе диска не совпадает с отверстием на диске) 

на 1 (отверстия совпадают), и далее — опять на 0. 

Если этот бит не будет менять своего значения — либо 

в дисководе нет диска, либо дверца открыта.


    Теперь мы легко можем указать искомый алгоритм:


прочитать значение регистра состояния. Пусть s1 — 

значение первого бита, s2 — значение шестого бита. 

пусть s3=1, если диск вращается, и 0, 

если диск не вращается. 

определить ситуацию по таблице: 

Табл. 3 s1 s2 s3 Номер ситуации 

0 0 0 2 

0 0 1 3 

0 1 0 2 

0 1 1 3 

1 0 0 1 

1 0 1 3 

1 1 0 2 

1 1 1 3 


    Расшифровка номера ситуации:


  1 — в дисководе нет диска.

  2 — диск есть, но дверца дисковода не закрыта.

  3 — диск есть и дверца дисковода закрыта.


    Примечание: с вероятностью примерно 3% ситуация 2 может 

быть распознана как ситуация 1 — это происходит, 

если индексное отверстие на диске случайно оказывается 

как раз напротив отверстия на корпусе дискеты.


    Ну что же, осталось только привести текст процедуры на ассемблере:


;***********************************************

;Процедура D_READY определяет, вставлен ли

;диск в дисковод и закрыта ли дверца дисковода.

;В регистре A возвращается число 1, 2 или 3,

;обозначающее номер ситуации:

;

; 1 - в дисководе нет диска.

; 2 - диск есть, но дверца дисковода не

;     закрыта.

; 3 - диск есть и дверца дисковода закрыта.

;

;   Примечание: с вероятностью примерно 3%

;ситуация 2 может быть распознана как

;ситуация 1.


D_READY XOR     A        ;Устанавливаем один и тот же

        LD      C,#3F    ;номер цилиндра

        CALL    TO_WG93  ;в регистре дорожки

        LD      C,#7F    ;и в регистре данных.

        CALL    TO_WG93


        LD      A,#18    ;Позиционирование. Головка не будет

        CALL    TO_1F    ;двигаться, но двигатель включится.

        CALL    READY    ;Ждем выполнения...

        CALL    STATUS   ;Читаем регистр состояния

        LD      B,A      ;и сохраняем в регистре B.


;Теперь читаем регистр состояния в цикле,

;чтобы определить, вращается ли диск.

;Если #300 раз будет считано одно и то же

;значение, значит, диск не вращается.

;

;  Примечание: число #300 подобрано опытным путем,

;исходя из максимального времени работы цикла

;(это чуть больше времени одного оборота диска,

;или 200 ms). Если процессор работает быстрее или

;диск вращается медленнее, число нужно увеличить.


        LD      HL,#300 ;счетчик

LOOP_D  PUSH    HL

        PUSH    BC

        CALL    STATUS

        POP     BC

        POP     HL

        DEC     HL

        CP      B         ;Сравниваем считанное и ранее

                          ;полученное значения.

        LD      A,1       ;Формируем 0-й бит регистра A.

        JR      NZ,DISK_R ;Если диск крутится.

        LD      A,H

        OR      L

        JR      NZ,LOOP_D ;Продолжаем считывать значение...


;Формируем в регистре A байт с таким содержимым:

;

;  Бит 0: 0 - диск не крутится, 1 - крутится.

;  Бит 1: то же, что было в 6-м бите регистра состояния.

;  Бит 2: то же, что было в 1-м бите регистра состояния.

;

;В результате в регистре A получаем число от 0 до 7,

;и далее определяем номер ситуации по таблице.


DISK_R  BIT     6,B

        JR      Z,READY1

        SET     1,A


;Установлено значение 1-го бита.


READY1  BIT     1,B

        JR      Z,READY2

        SET     2,A


;В A получено нужное значение.


READY2  LD      (THIS_B+2),A ;Модифицируем команду.


;Но сначала отключаем дисковод:


        XOR     A         ;Эти команды,

        CALL    TO_1F     ;в принципе,

        LD      A,#D0     ;можно

        CALL    TO_1F     ;выбросить.


;Эта команда соответствует LD A,(IX+0)...LD A,(IX+7):


        LD      IX,TABL

THIS_B  LD      A,(IX)       ;Получили номер ситуации.

        EI

        RET


;Таблица для определения номера ситуации:


TABL    DB      2,3,2,3,1,3,2,3


;***************************************

;Вспомогательные процедуры:


TO_1F   LD      C,#1F

TO_WG93 LD      IX,#2A53

        JR      TO_DOS


READY   LD      IX,#3EF5

TO_DOS  PUSH    IX

        JP      #3D2F


;***************************************

;Процедура STATUS возвращает содержимое

;регистра состояния.

;Вход:  A - содержимое регистра дорожки,

;       B - содержимое регистра сектора,

;       которые будут установлены после

;       выхода из процедуры.

;Выход: A - значение, считанное из порта #1F.

;Прерывания после выхода запрещены!


STATUS  DI

        LD      C,#7F    ;A=N цилиндра.

        CALL    TO_WG93  ;В регистр данных.


        LD      (RG_D+1),A  ;дорожка

        LD      A,B

        LD      (RG_S+1),A  ;сектор


;Сохраняем содержимое ячеек, которые

;могут быть испорчены:


        LD      A,(#5D0E)

        LD      (ST1+1),A

        LD      A,(#5D0C)

        LD      (ST2+1),A

        LD      A,(#5CB6)

        LD      (ST3+1),A

        LD      A,(#5D1F)

        LD      (ST4+1),A

        LD      A,(#5C3A)

        LD      (ST5+1),A

        LD      A,(#5D17)

        LD      (ST6+1),A

        LD      HL,(#5D1A)

        LD      (ST7+1),HL

        LD      HL,(#5D1C)

        LD      (ST8+1),HL

        LD      HL,(#5CF8)

        LD      (ST9+1),HL


;Устанавливаем содержимое некоторых

;ячеек для правильной работы:


        LD      A,#FF

        LD      (#5D0C),A

        LD      (#5D1F),A

        DEC     A

        LD      (#5D0E),A

        LD      A,#F4

        LD      (#5CB6),A


        LD      HL,S_SPEC

        LD      (#5D1A),HL

        LD      HL,0

        ADD     HL,SP

        LD      DE,-12

        ADD     HL,DE

        LD      (#5D1C),HL


        LD      A,0     ;0 в регистр

        LD      C,#3F   ;дорожки.

        CALL    TO_WG93

        LD      A,#0A   ;#A в регистр

        LD      C,#5F   ;сектора.

        CALL    TO_WG93

        LD      D,1

        LD      IX,16179

        CALL    TO_DOS  ;Определили #1F.


;Теперь восстанавливаем содержимое

;регистров дорожки и сектора:


RG_D    LD      A,0

        LD      C,#3F

        CALL    TO_WG93

RG_S    LD      A,0

        LD      C,#5F

        CALL    TO_WG93


;Восстанавливаем ранее запомненное

;содержимое ячеек:


ST1     LD      A,0

        LD      (#5D0E),A

ST2     LD      A,0

        LD      (#5D0C),A

ST3     LD      A,0

        LD      (#5CB6),A

ST4     LD      A,0

        LD      (#5D1F),A

ST5     LD      A,0

        LD      (#5C3A),A

ST6     LD      A,0

        LD      (#5D17),A

ST7     LD      HL,0

        LD      (#5D1A),HL

ST8     LD      HL,0

        LD      (#5D1C),HL

ST9     LD      HL,0

        LD      (#5CF8),HL


        LD      A,B

        RET


;Сюда будет передано управление, если

;0-й бит регистра состояния равен 1:


S_SPEC  POP     BC     ;Содержимое порта.

        LD      HL,(#5D1C)

        LD      DE,12  ;Восстанавливаем

        ADD     HL,DE  ;указатель

        LD      SP,HL  ;стека.

        JR      RG_D


*  *  *

    Вам наверняка встречались программы, контролирующие 

смену диска в дисководе (например, Jemmini Commander). 

В них проверяются именно указанные выше биты системного 

регистра (в принципе, достаточно следить за изменением лишь 

какого-нибудь одного из двух битов). 

Вот простейший алгоритм работы такой программы:


Прочитать значение регистра состояния. 

Если первый бит равен 1, перейти к шагу 4. 

N="Диск есть". 

Прочитать значение регистра состояния. 

Если первый бит равен 0, перейти к шагу 3. 

N="Диска нет". 

Прочитать значение регистра состояния. 

Если первый бит равен 1, перейти к шагу 5, иначе к шагу 2. 

    Примечание: программа работоспособна при условии, 

что индексное отверстие на диске не находится напротив 

соответствующего отверстия на корпусе дискеты 

(это связано с использованием первого бита регистра состояния).


    Кстати, у такой программы есть одна особенность, 

связанная с чтением регистра состояния. 

Чтобы прочесть его значение, нужно, чтобы двигатель 

дисковода работал. Поэтому используется следующий приём: 

выполняется команда позиционирования на дорожку, 

номер которой уже записан в регистр дорожки. 

При этом головка не будет двигаться, но двигатель дисковода включится. 

После окончания выполнения этой команды читается регистр состояния. 

Сразу после этого двигатель выключается, например, 

с помощью записи 0 в порт #FF (или в порт #1F). 

Между двумя последовательными чтениями регистра состояния делается пауза, обычно 1/50 секунды. 

При этом двигатель не успевает раскрутиться, и лампочка дисковода не загорается. 

Но если внимательно присмотреться, видно, 

что лампочка всё же чуть-чуть светится 

(яркость свечения обратно пропорциональна длине паузы). 

Так вот, на некоторых дисководах (а именно — на ЕС 5323.01) 

было замечено, что при работе программ, контролирующих смену диска, 

лампочка постоянно горит и двигатель работает.


    Привожу текст небольшой демонстрационной программы, 

контролирующей смену диска по вышеуказанному алгоритму. 

Программа выводит на экран сообщения «DISK PRESENT» и «DISK NOT PRESENT». 

Выход — по нажатию любой клавиши (когда диск присутствует).


        CALL    3435    ;CLS

        LD      A,2

        CALL    5633


M_1     CALL    READ_S

        BIT     1,A

        JR      NZ,M_4


M_2     LD      A,1

        LD      (N),A

        LD      DE,TEXT1

        LD      BC,23

        CALL    8252    ;"DISK PRESENT"


M_3     XOR     A

        IN      A,(254)

        AND     31

        CP      31

        RET     NZ      ;Если нажата клавиша, выходим.


        CALL    READ_S

        BIT     1,A

        JR      Z,M_3


M_4     XOR     A

        LD      (N),A

        LD      DE,TEXT2

        LD      BC,23

        CALL    8252    ;"DISK NOT PRESENT"


M_5     CALL    READ_S

        BIT     1,A

        JR      NZ,M_5

        JR      M_2


N       DB      0       ;1 - есть диск, 0 - нет.

TEXT1   DB      22,0,0,16,7,17,0,"DISK PRESENT    "

TEXT2   DB      22,0,0,16,7,17,0,"DISK NOT PRESENT"


;***************************************

;Процедура READ_S осуществляет чтение

;регистра состояния. Перед чтением

;производится включение двигателя

;дисковода, а после чтения - выключение

;и пауза в 1/50 секунды.


READ_S  XOR     A        ;Устанавливаем один и тот же

        LD      C,#3F    ;номер цилиндра

        CALL    TO_WG93  ;в регистре дорожки

        LD      C,#7F    ;и в регистре данных.

        CALL    TO_WG93


        LD      A,#18    ;Позиционирование. Головка не будет

        CALL    TO_1F    ;двигаться, но двигатель включится.

        CALL    READY    ;Ждем выполнения...

        CALL    STATUS   ;Читаем регистр состояния.

        EI               ;Разрешаем ранее запрещенные прерывания.

        PUSH    AF

        XOR     A

        CALL    TO_1F    ;Выключаем двигатель.

        LD      A,#D0

        CALL    TO_1F

        HALT             ;Пауза в 1/50 секунды.

        POP     AF

        RET


;***************************************

;Вспомогательные процедуры:


TO_1F   LD      C,#1F

TO_WG93 LD      IX,#2A53

        JR      TO_DOS


READY   LD      IX,#3EF5

TO_DOS  PUSH    IX

        JP      #3D2F


<Далее следует текст процедуры STATUS> 









 3.4.4 Системные переменные TR-DOS


    Дисковой  операционной системе  также необходимо  где-то

хранить  информацию о  дисководах и режимах работы, файлах и

т.п.

    Для  этого она  используется область  начиная  с  адреса

#5CB6 (23734)

    Для системных переменных TR-DOS еще не сложились имена в

технической  литературе (точнее  говоря их  вообще никто  не

давал),  поэтому я  здесь наберусь  наглости дать  эти имена

сам.


UNIT_F, байт, #5CB6, X, 23734

    Используется  при наличии  INTERFACE  1.  Если  значение

равно  #F4, то  область остается  на старом месте. Если байт

равен    0,   то   происходит   проверка   байта   #5D18   и

соответствующая операция.


S_RET, байт, #5CC2, , 23746

Содержит  код команды  RET. Используется  TR-DOS для  вызова

подпрограмм Бесйик-ПЗУ.


MODE_A, байт, #5CC8, ,23752

Код режима работы дисковода А.

бит 7=1, если дисковод 80-ти дорожечный.

бит 1=1, если дисковод двухсторонний

бит 0=0, если 80-ти дорожечный дисковод используется как 40

         дорожечных


MODE_B, байт, #5CC9,X, 23753

Код режима работы дисковода В. См MODE_A


MODE_C, байт, #5CCA,X, 23754

Код режима работы дисковода С. См MODE_A


MODE_D, байт, #5CCB,X, 23755

Код режима работы дисковода D. См MODE_A


C_SERD, байт, #5CCC, X, 23756

Текущий сектор при чтении каталога


READY, байт, #5CCD, X, 23757

Содержит #80 при готовности текущего дисковода.


TRMODE, байт, #5CCE,X, 23758

Режим работы:0 чтение/#FF запись


DOS_ER, байт, #5CD6, X, 23766

Содержит #FF, если команда не выполнена


X_BYTE, слово, #5CD7, X, 23767

    Для  файлов типа  BASIC или  BYTE -  адрес старта. Также

содержит число дорожек сразу после тестирования дисковода.


CHADD2, слово, #5CD9, X, 23769

Внутренний  аналог CH_ADD.  Также примежуточная  длина файла

типа BYTES или BASIC


LENPRG, слово, #5CDB, X, 23771

Длина бейсик программы


NAME_F, 8 байт, #5CDD, , 23773

Имя файла


TYPE_F, байт, #5CE5, , 23781

Тип файла


PAR_F, слово, #5CE6, , 23782

Параметры файла:

Для Бейсика - длина бейсик программы без переменных

Для кодов   - стартовый адрес


LEN_F, слово, #5CE8, , 23784

Для файла


SEC_F, байт, #5CEA, , 23786

Размер файла в секторах


FIR_SE, байт, #5CEB, , 23787

Номер первого сектора файла


FIR_TR, байт, #5CEC, , 23788

Номер первой дорожки файла


A_UNIT, байт, #5CEF, X, 23791

Содержит 1 если есть INTERFACE 1


CUR_SEC, байт, #5CF4, X, 23796

    Номер текущего сектора


T_DRV, байт,#5CF6, , 23798

Дисковод  для временной  операции (от 0 до 3). Например LOAD

"A:HELP.ASM" CODE


DOS_ON, слово, #5CF7, , 23799

Обнуляется при возврате из TR-DOS


DBLDRV, байт, #5CF8, , 23800

Дисковод  при  операциях  с  двумя  дисководами,  в

противном случае #FF.


DBLMOD, байт, #5CF9, , 23801

    Режим  работы при  работе с  двумя дисководами  (признак

операции  READ/VERIFY). #FF  - VERIFY,  00 READ.  Сюда также

заноситься номер дискова при внутренней операции 7 (CAT).


TIME_A, байт, #5CFA, , 23802

Время перемещения головки дисковода А.


TIME_B, байт, #5CFB, ,23803

Время перемещения головки дисковода В.


TIME_C, байт, #5CFC, , 23804

Время перемещения головки дисковода С.


TIME_D, байт, #5CFD, ,23805

Время перемещения головки дисковода D.


WG_CODE, байт, #5CFE, X, 23806

Код команды для контроллера дисковода ВГ93


SEC_WRK, байт, #5CFF, X, 23807

Номер сектора для подпрограмм записи, чтения


BUF, слово, #5D00, X, 23808

Текущий адрес буфера


HID_HL,слово,#5D02, X,23810

Временно хранит HL при необходимости


HID_DE, слово, #5D04, X, 23812

Временно хранит DE при необходимости


CH_NAME, байт, #5D06, , 23814

Количество  символов, по  которым осущесвляется  поиск имени

файла.  Нормальное значение - 9, но вы можете его уменьшить.

Увеличивать не рекомендую.


DELF, байт, #5D07, X, 23820

Количество удаленных файлов


DELN,байт,#5D08,X,23816

Первый символ удаленного файла


WRKBUF, слово,#5D0C,X,23820

Флаф  наличия рабочего  буфера TR-DOS  (257  байт  с  адреса

23846):

FF - отсутствует

00 - существует


FL_COM,байт,#5D0E,X,23822

Флаг принадлежности команд бейсика

FF - бейсик,

любое другое значение - TR-DOS


ER_FL,байт,#5D0F,X,23823

Код  ошибки TR-DOS.  При равенстве  0 выводит пустую строку,

иначе пустую строку (подпрограмма по адресу #20EF)


ER_HIGH,байт,#5D10,X,23824

Старший  байт ошибки.  При переходе в TR-DOS обнуляется. При

вызове подпрограмм TR-DOS следует обнулять принудительно.


COMADD,слово,#5D11,X,23825

Адрес строки команды TR-DOS. При вызове:

15616 - хранит E_LINE

15619 - хранит CH_ADD


ERRSP2, слово,#5D13,X,23827

Копия  ERR_SP.При равенстве  старшего байта  АА  выполняется

команда RUN "boot" и в #5D18 заноситься #FE


MES_ON,байт,#5D15,X,23829

При   равенстве  00  печатает  сообщения  TR-DOS,  иначе  не

печатает.


SYSREG,байт,#5D16,,23830

Копия системного регистра (Микросхема 555ТМ9)


X_FL,байт,#5D17,,23831

При  не равенстве  АА рисует  заставку, при  равенстве FF не

попадает на ошибку при чтениее неверного адресного маркера.


INT_ON,байт,#5D18,X,23832

Используется  при подключенном INTERFACE1. Если равно FF, то

меняются  блоки в памяти по адреса 23747 и 23959 длиной в 45

байт. При вызове TR-DOS заноситься FF


DRVDEF,байт,#5D19,,23833

Дисковод по умолчанию (0-3)


RETADD,слово,#5D1C,X,23834

Адрес процедуры завершения. Нормальное значение #0201


HID_SP,слово,#5D1C,X,23836

Временно сохраняет регистр SP


TRLINE,3 байта,#5D20,,23840

Первые три символа введенной строки







TRDOS MORE...

trdos




11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

┌──────────────────────────────┐

│                              │

│    TR-DOS ДЛЯ НАЧИНАЮЩИХ     │

│                              │

└──────────────────────────────┘


          В.Сироткин.


Продолжение.  Начало см. в ZX

РЕВЮ 1996 NN 1-2, 4-5, 6, 7-8,

ZX РЕВЮ 1997 NN 1-2.


  ПРОГРАММИРОВАНИЕ КОНТРОЛЛЕРА.

  ─────────────────────────────

   Из предыдущего  материала  мы

узнали, что компьютер общается с

контроллером   дисковода   через

специально выделенные порты. Так

как с этими портами нам и  пред-

стоит  в  дальнейшем   работать,

здесь еще раз  приводится  крат-

кая табличка  назначения  портов

ввода/вывода/управления контрол-

лера ТРДОС.


      ┌─────────────────────────────────────────────────┐

      │ ПОРТ #1F  - Регистр Состояния - ЧТЕНИЕ          │

      │ ПОРТ #1F  - Регистр Команд    - ЗАПИСЬ          │

      │ ПОРТ #3F  - Регистр Дорожки   - ЗАПИСЬ / ЧТЕНИЕ │

      │ ПОРТ #5F  - Регистр Сектора   - ЗАПИСЬ / ЧТЕНИЕ │

      │ ПОРТ #7F  - Регистр Данных    - ЗАПИСЬ / ЧТЕНИЕ │

      │                                                 │

      │ ПОРТ #FF  - Регистр Управления -ЗАПИСЬ          │

      │                                                 │

      │ - 0,1 биты - номер дисковода ('A'= 0,0)         │

      │ - 2   бит  - сброс ВГ93      (при=0)            │

      │ - 3   бит  - подготовиться   (при=1)            │

      │                                                 │

      │ - 4   бит  - сторона диска   (при=1 верх)       │

      │ - 5   -//--------------------//---------        │

      │ - 6   бит  - плотность       (при=0 двойная)    │

      │ - 7   -//--------------------//---------        │

      │                                                 │

      │ ПОРТ #FF  - Регистр Управления - ЧТЕНИЕ         │

      │                                                 │

      │ - 6  бит - строб байта данных (при =1 есть      │

      │                                      данные)    │

      │ - 7  бит - готовность         (при =1 готов)    │

      └─────────────────────────────────────────────────┘


   Как нам уже  известно, прямым

путем записать или  считать  ин-

формацию по этим портам команда-

ми OUT или IN  нам  не  удастся,

так как эти порты подключаются к

компьютеру  только  тогда, когда

включена ПЗУ  ТРДОС  и, соответ-

ственно, блокированы  все  порты

компютера.

   Возникает  закономерный  воп-

рос: "А что же делать ?". Дело в

том, что  в  адресном  простран-

стве ПЗУ  ТРДОС, которое  совпа-

дает с адресами, задействованны-

ми на перехват и включение ТРДОС

контроллера  (а как мы помним из

первой главы  -  это  промежуток

адресов  с #3D00 по #3DFF), есть

точка входа, как раз  предназна-

ченная для таких случаев.  (Спа-

сибо  программистам  -  хоть  ее

предусмотрели !)


              * * *

       ЭТО ТОЧКА С АДРЕСОМ

         #3D2F или #3D30.

              * * *


А выглядит эта точка в ПЗУ ТРДОС

вот так:140.

          #3D2F    NOP

          #3D30    RET2


   "Ну и что?  Как она может нам

помочь? Как за то время  включе-

ния ПЗУ ТРДОС нам  дать  команду

портам?" - воскликните вы! Очень

просто! Перед вызовом этой  точ-

ки командой  'JP' мы  занесем  в

стек сначала адрес возврата, за-

тем адрес подпрограммы ПЗУ  ДОС,

где есть нужная нам  подпрограм-

ма. По  команде  'RET'  в  точке

#3D30 процессор возьмет из стека

адрес  и  исполнит  подпрограмму

ПЗУ ТРДОС, т.к.  в  этот  момент

она будет подключена.

   Единственное   условие:   эта

подпрограмма в ПЗУ должна  окан-

чиваться командой 'RET' для  то-

го, чтобы все вернулось  на  тот

адрес возврата, который  мы  за-

несли в стек первым.

   Короче говоря, если нам  надо

исполнить какую-нибудь  подпрог-

рамму в ПЗУ ТРДОС, то наши  дей-

ствия:


   1) Занести в стек адрес, куда

вернется программа после  выхода

из ПЗУ ДОС.

   2) Занести в стек  адрес нуж-

ной подпрограммы ПЗУ ДОС.

   3) Дать команду JP #3D2F (или

#3D30).


   Такой способ вызова называет-

ся  "Косвенная  адресация  через

стек".

   Если нам необходимо, чтобы  в

ПЗУ исполнились две -  три  под-

программы, то в стек  загружают-

ся (после адреса возврата) два -

три адреса подпрограмм.  Послед-

ним  в стек  должен быть помещен

адрес подпрограммы, которая  бу-

дет выполняться первой!

   Вроде бы  ничего сложного, но

вот тут-то и начинаются неприят-

ности...

   Дело в том, что если содержи-

мое точки вызова #3D30 для  всех

версий ТР ДОС одинаково, то  АД-

РЕСА  ПОДПРОГРАММ-УТИЛИТ   ТРДОС

ДЛЯ РАЗЛИЧНЫХ ВЕРСИЙ  РАЗЛИЧНЫ !

Для версии 5.01 это одни адреса,

а для 5.03 это другие адреса.

   И вот, если Вы составите свою

программу, основываясь на  адре-

сах версии, которая зашита в ПЗУ

Вашего  контроллера, то  это  не

значит, что программа будет  ра-

ботать с контроллером  приятеля,

у которого другая версия ТР ДОС.

   Может статься, что в 50% слу-

чаев Ваша программа  просто  за-

виснет или, того хуже, -  запор-

тит Вашему  приятелю  данные  на

диске.

   Пример  из  практики:  автору

этой книги попалась адаптирован-

ная  под  диск программа 'WORD'-

текстовый редактор, которая была

рассчитана на версию ТРДОС-5.03.

(Загрузка самого редактора  тоже

производилась через подпрограммы

ПЗУ ТРДОС).

   Запущенная   с   контроллером

версии 5.01, эта программа уже в

процессе загрузки "успешно" фор-

матировала нулевую и первую  до-

рожки диска.

   После чего так же успешно за-

висала. Представьте себе: Вы бе-

рете диск с целым пакетом  прог-

рамм, запускаете редактор, а по-

том...диск приходится форматиро-

вать заново.

   И самое главное, что  никаких

надписей, с какой версией  ТРДОС

этот редактор работает...  Хоро-

шая адаптация - нечего сказать !

   При  ближайшем   рассмотрении

оказалось,  что  адреса  в   ПЗУ

5.03 подпрограммы "чтения секто-

ра"  в версии 5.01 лежат по дру-

гим адресам, а по этим адресам в

версии 5.01 расположена подпрог-

рамма "форматирование".


   Так вот правило НОМЕР ОДИН:


 Если Вы программируете  на  са-

 мом низком уровне, т.е. с выхо-

 дом на  подпрограммы  ТРДОС, то

 Ваша программа  должна  опреде-

 лять (или  запрашивать)  версию

 ТРДОС  и  затем  корректировать

 адреса вызовов подпрограмм ПЗУ.

 В крайнем случае Ваша  програм-

 ма  должна  выводить  на  экран

 НОМЕР  ВЕРСИИ  ТРДОС, на  какую

 программа адаптирована.

  Это правило хорошего тона !!!


   Далее в  примерах  будут при-

сутствовать адреса  и версии  ТР

ДОС 5.01, и  версии 5.03.


 За основу приняты  адреса  вер-

 сии 5.01 !!!  А  адреса  версии

 5.03  будут  даваться  рядом, в

 угловых скобках: < адрес >.


   Итак, точка входа у нас есть,

остались только адреса  подпрог-

рамм, которые бы работали с нуж-

ными портами.

   Таких подпрограмм в ПЗУ  мно-

жество. Но они так тесно  увяза-

ны друг с другом, что если выде-

лить  только  те, которые  после

команд  'IN'  или  'OUT'   сразу

имеют  команду  'RET', то  таких

подпрограмм окажется совсем нем-

ного...140.


  1. Запись в порт #1F (регистр команд).


Адреса в ПЗУ      Мнемоника

──────────────────────────────────────────────

#2F79  < #2FC3 >  OUT (#1F),A  ; подать команду

                  RET          ; вернуться



  2. Запись в порт #1F (регистр команд).


#3ED5  < #3EDF>   OUT (#1F),A   ; подать команду

                  LD A,(#5CD1)  ; опросить ОЗУ

                  CP #FF        ; вернуться, если

                  RET Z         ; там байт #FF



  3. Запись в порт #3F (регистр дорожки).


#1DFE  < #1E3A >  OUT (#3F),A

                  RET



  4. Запись в порт #3F (регистр дорожки).


#3E8B  < #3E95 >  OUT (#3F),A   ; номер дорожки

                  LD A,(#5CCD)  ; опросить ОЗУ

                  OR A          ; венуться, если

                  RET Z         ; там байт 00



  5. Запись в порт #FF (управление).


#1FB7  < #1FF3 >  OUT (#FF),A

                  RET



  6. Запись в порт #FF (управление).


#2EC2  < #2F0C >  OUT (#FF),A

                  RET



  7. Запись в любой порт  по регистру 'C'.


#2A09  < #2A53 >  OUT (C),A   ; номер порта в 'C'

                  RET2


   Вы, наверное,  заметили,  что

многих портов в этих подпрограм-

мах не хватает, а чтения из пор-

тов нет вообще!  Чтобы заставить

контроллер что-то  делать, необ-

ходимо  выполнить ряд действий в

совокупности.  Одной командой, в

большинстве  случаев,  не  обой-

тись.

   Весь процесс обмена  ДИСК <->

ПРОЦЕССОР состоит из 2,3,4  под-

программ ПЗУ, которые  выполняют

ряд  взаимосвязанных    действий

сразу с несколькими портами.  Но

даже с теми  подпрограммами, ко-

торые у нас уже есть, можно  вы-

полнить определенные действия.

   Например:  воздействовать  на

порт управления (#FF) и дать ко-

манду  первого  типа  (адреса  в

примерах - для версии 5.01).147.


  1. Сброс микросхемы ВГ93 и переход на 0 дорожку.


START  LD IX,ENDE     ; адрес возврата -> в стек

       PUSH IX

       LD IX,#1FB7    ; адрес п/п ДОС

                      ;  (запись в порт #FF)

       PUSH IX        ; занести в стек

       LD A,0         ; управляющая команда СБРОС

       JP #3D2F       ; исполнить п/п в ПЗУ

ENDE   RET            ; выйти вообще

2

!!! Кстати, если дать такую  ко-

манду, то потом, при нажатии  на

'МАГИК'  кнопку, вместо  'магик'

файла  Вы  получите  испорченный

диск, т.к. микросхема будет пос-

тоянно НЕ ГОТОВА по порту #FF и,

чтобы вывести ее из  этого  сос-

тояния, необходимо не только по-

дать в порт #FF 'готовность', но

и подать команду ПРЕРЫВАНИЕ, ко-

торая начисто отсутствует в под-

программе   обработки    'МАГИК'

кнопки !!!147.


  2. Выбор номера диска, плотности, подача готовности

     и выбор верхней стороны диска...


START  LD IX,ENDE       ; адрес возврата - в стек

       PUSH IX

       LD IX,#1FB7      ; адрес п/п ДОС (запись в

                        ; порт #FF)

       PUSH IX          ; в стек

       LD A,%00111100   ; управляющая команда:

                        ; диск 'A', и т.д

       JP #3D2F         ; исполнить п/п в ПЗУ

ENDE   RET              ; выйти вообще



  3. Поиск нужного цилиндра на диске.


START  LD C,#FF     ; порт  #FF в регистр 'C'

       LD A,#3C     ; готовность , диск A и тд.

       CALL TRDOS   ; записать в порт #FF


       LD C,#7F     ; регистр данных

       LD A,5       ; команда - найти 5-й  цилиндр

       CALL TRDOS   ; записать в регистр данных


       LD C,#1F     ; регистр команд

       LD A,#1C     ; команда ПОИСК ЦИЛИНДРА с

                    ; опущенными головками, с провер-

                    ; кой и с минимальной задержкой

       CALL TRDOS   ; дать команду на поиск


ENDE   RET          ; выход из программы


;- - - - - подпрограмма записи данных в порт - - -


TRDOS  LD IX,#2A09  ; адрес п/п в ПЗУ 'запись в

                    ; порт по регистру 'C' байта

                    ; из регистра 'A'

       PUSH IX

       JP #3D2F     ; исполнить и вернуться потом,

                    ; как из подпрограммы2


   Таким методом  можно  записы-

вать данные в  управляющий  порт

#FF, в регистры  ДОРОЖКИ, СЕКТО-

РА  и подавать команды для ВГ93,

но организовать диалог (т.е. об-

мен данными и  проверку) процес-

сор <-> контроллер очень затруд-

нительно.

   Вообще, обмен  данными  между

контроллером и процессором  дол-

жен идти  по следующему алгорит-

му:

    1.  Даем байт  управления  в

порт #FF. Так как этот порт под-

ключен к триггеру с защелкой, то

эта  информация  сохраняется  до

прихода следующего байта в  этот

порт  (выбираем  дисковод,  даем

подготовительную     готовность,

сторону диска и плотность).

    2.  Даем  в  РЕГИСТР команд,

порт #1F, байт КОМАНДЫ и  обяза-

тельно самая первая  команда пе-

ред командами  ЧТЕНИЕ  -  ЗАПИСЬ

должна  быть  команда    ПЕРВОГО

ТИПА, с модификатором  'опустить

головку на диск' (это связано со

схемотехническими  особенностями

всего контроллера ТРДОС).

    3. Начинаем опрашивать  порт

#FF на 7-й бит  (бит  готовности

микросхемы ВГ93) до тех пор, по-

ка  там  не установится ЕДИНИЦА,

т.е. наша команда выполнена.

    4. Если команда была  связа-

на с ЧТЕНИЕМ  или  ЗАПИСЬЮ, т.е.

команды типа 2 и типа 3, то сов-

местно с опросом  бита  7  порта

#FF необходимо перед каждым бай-

том ДАННЫХ, идущим  с диска  или

на диск, опрашивать 6-й бит пор-

та #FF  (строб  данных).  И если

бит 6 установился в  ЕДИНИЦУ, то

принять или передать байт в порт

#7F (регистр данных).

    5.  Как только бит  7  порта

#FF установится в ЕДИНИЦУ, можно

приступать  к  записи  следующей

команды (перед подачей следующей

команды можно дать небольшую за-

держку пустым циклом).

    6.  Опросить регистр СОСТОЯ-

НИЯ ВГ93 (порт #1F) и определить

правильность  выполненной коман-

ды.  Если  команда выполнена без

ошибок, и все в норме, то из ре-

гистра СОСТОЯНИЯ  считается байт

#80.

    7. Если в регистре СОСТОЯНИЯ

устанавливаются биты, то в зави-

симости от того, какой бит уста-

новился,  принимаются   соответ-

ствующие действия.

   Например:  при  команде  'за-

пись':

 - Если  установлен  бит  6,  то

придется прервать операции пода-

чей команды 'ПРЕРЫВАНИЕ'  и  вы-

вести  надпись  на  экран: "ДИСК

ЗАЩИЩЕН".

 - Если выставился бит 2  (поте-

ря данных), то придется или пов-

торить операцию заново, или вый-

ти с надписью на экране  "ошибка

записи".

 - Если бит 0 установлен в  еди-

ницу, то это значит, что  диско-

вод занят и придется ждать, пока

он не освободится.

   Поначалу  данная   последова-

тельность  кажется  длинной    и

очень сложной, но так как в  ПЗУ

ТРДОС эти подпрограммы уже  есть

в совокупности, то весь  процесс

выльется в  вызов  2-х  или  3-х

соответствующих  подпрограмм че-

рез стек из ПЗУ.

   Необходимо запомнить еще  од-

но правило:  правило  последова-

тельности подачи команд.


       ПРАВИЛО НОМЕР ДВА:


   Из-за  схемотехнической  осо-

бенности контроллера ТРДОС вклю-

чение двигателя дисковода и при-

жатие  головки  к диску произво-

дится только  командами  ПЕРВОГО

ТИПА, с модификатором  МАГНИТНАЯ

ГОЛОВКА В РАБОТЕ (т.е. когда го-

ловка  прижата  к  диску  и диск

раскручен).

   Третий бит  кода команды дол-

жен быть равен 1. Так как диско-

вод является медленнодействующим

устройством, то  после  выполне-

ния команды первого типа остает-

ся достаточно времени для подачи

следующих команд  ЧТЕНИЯ ИЛИ ЗА-

ПИСИ.

   Давайте рассмотрим  несколько

примеров  дисковых  процедур  из

ПЗУ.


           ПРИМЕР 1.

Подпрограмма  ЧТЕНИЯ  из  порта.

   Номер порта в регистре 'C'.

   Перед вызовом данной процеду-

ры НЕОБХОДИМО:  если  считывание

производится из регистра  данных

(т.е. считывание с диска), то  в

регистр КОМАНД (порт #1F)  нужно

записать команду, а в порт #FF -

записать готовность.

   Естественно,  головки   диска

должны быть установлены на  нуж-

ную дорожку и прижаты к диску.

   (Адреса в примерах для  ТРДОС

5.01). В регистр 'HL'  поместить

адрес ОЗУ, куда  будем считывать

информацию. В регистр 'C' -  но-

мер порта.140.


#3FDB < #3FE5 > IN A,(#FF) ; опрос выполнения и

                           ; строба данных

                AND #C0

                JR Z,#3FDB ; если не выполнено и нет

                           ; строба, то опять читаем

                           ; порт #FF

                RET M      ; выполнено - выход

                INI        ; чтение байта из порта

                           ; в адрес M (HL)

                JR #3FDB   ; повторим, если групповая

                           ; операция

2

   Вообще-то эта процедура пред-

назначена  для   чтения  массива

данных  с диска (из  СЕКТОРа или

ДОРОЖКИ), но ее можно  использо-

вать и для чтения  из  регистров

ДОРОЖКИ, УПРАВЛЕНИЯ  и  СЕКТОРА,

если в регистр 'C' занести соот-

ветствующий  порт. В этом случае 

подпрограмма сработает ОДИН раз,

и у Вас в адресе M (HL) окажется 

считанный байт.


    ВНИМАНИЕ!  РЕГИСТР СОСТОЯНИЯ

(ПОРТ #1F) ЭТОЙ ПРОЦЕДУРОЙ ОПРА-

ШИВАТЬ НЕЛЬЗЯ.   Так как при его

опросе бит ГОТОВНОСТЬ порта  #FF 

сбросится в 0 и все зациклится !


           ПРИМЕР 2.

Подпрограмма ЗАПИСЬ В ПОРТ.  Но-

мер порта в регистре 'C'.

   Эта процедура является обрат-

ной копией предыдущей процедуры.

Перед вызовом  данной  процедуры

НЕОБХОДИМО:

   Если  запись  производится  в

регистр данных  (т.е. запись  на

диск), то в регистр КОМАНД (порт

#1F) нужно записать команду, а в

порт #FF - записать  готовность.

Естественно, головки диска  дол-

жны быть установлены  на  нужную

дорожку и прижаты к диску. В ре-

гистр 'HL' поместить адрес  ОЗУ,

ОТКУДА будем записывать информа-

цию. В регистр 'C'- номер порта.140.


#3FC0 < #3FCA > IN A,(#FF)  ; опрос выполнения и

                            ; строба данных

                AND #C0

                JR Z,#3FC0  ; если не выполнено и

                            ; нет строба,то опять

                            ; читаем порт #FF

                RET M       ; выполнено - выход


                OUTI        ; запись байта в порт

                            ; из адреса  M (HL)

                JR #3FDB    ; повторим, если группо-

                            ; вая операция

2

   Этой   процедурой,   конечно,

можно  записать  байт  в   любой

порт, но для этого есть  другие,

менее длинные  процедуры (смотри

выше).

   Сразу же  возникает  законный

вопрос: "А как все  же  опросить

порт #1F, т.е.  Регистр  Состоя-

ния ?"

   Дело в том, что программисты,

которые писали ТРДОС, не предус-

мотрели  возможности  без  помех

опросить этот регистр  из  прог-

рамм  пользователя.  Все  опросы

порта #1F в ПЗУ тесно увязаны  с

другими  подпрограммами  чтения-

записи.

   Есть, правда, точка по адресу

#3F28 < #3F32 >, вызвав которую, 

можно опросить  Регистр  Состоя-

ния.  Но там сразу же происходит

и проверка битов с переходом  на 

адреса печати сообщений об ошиб-

ках и сбоях диска.

   Эту точку входа можно исполь-

зовать в  программах, в  которых

не  нарушена  системная  область 

BASIC'а  и  TRDOS, так  как  при  

возникновении каких-либо  ошибок  

система будет вызывать программу 

печати сообщений на экран.

   А если область системных  пе-

ременных нарушена ???  Есть  два

способа  выйти  из этого положе-

ния.

  Первый - если в программе про-

исходит ЧТЕНИЕ с диска, то после 

прочтения   сектора   (секторов)

произвести  подсчет  контрольной 

суммы  считанного  блока и срав-

нить с той  суммой, которая  из-

вестна (заранее просчитана).

   Если же происходит ЗАПИСЬ  на

диск, то тут придется или  запи-

сывать 2 раза один и тот же сек-

тор  (для уверенности), или  тут

же считать этот сектор обратно в

какое-нибудь свободное  место  в

ОЗУ и сравнить считанный блок  с

тем, который пытались  записать.

И если они не идентичны, то про-

бовать записать еще раз.  Как Вы 

понимаете, это  не лучший  выход

из положения !

   Второй способ - довольно  эк-

зотический, но очень эффективный  

и  позволяет   опросить  Регистр

Состояния в любое время из прог-

раммы пользователя.  Этот способ

основан на методе ВТОРОГО ПРЕРЫ-

ВАНИЯ! ("Вот так-так" - восклик-

нет внимательный читатель и нач-

нет  искать  те страницы  книги,

где написано, что ТРДОС не любит  

прерывания  ДВА.  Все  верно, не 

ищите и не торопитесь с выводами 

и руганью в адрес автора. Дело в

том, что действительно, большин-

ство подпрограмм ТРДОС  "боятся"

второго прерывания.  Это те под-

программы, которые  ответственны  

за  чтение/запись информации  на

диск.

   Мы же не будем  обращаться  к

диску, а опросим всего лишь порт

Состояния ВГ93). Итак...  В  ПЗУ

ТРДОС по адресу #2D3D < #2D87  >

есть  следующая   последователь-

ность команд.140.


;--подпрограмма 'опрос порта #1F'  #2D3D < #2D87 >


#2D3D < #2D87 > IN A,(#1F)   ; прочитать порт #1F

                AND #7F      ; выделим все уста-

                             ; новленные биты

                RET Z        ; вернемся, если НЕТ

                             ; ошибок

                DEC D        ; уменьшить регистр D

                PUSH HL      ; HL в стек

                PUSH DE      ; DE в стек

                JR NZ,#2D31  ; если регистр

                             ; 'D' <> 0 ,то

                             ; перейти выше,

                             ; а нам это не надобно

                HALT         ; Ждать прихода преры-

                             ; вающих импульсов INT

                ...          ; Далее

                             ; нас не интересует..

2

   Воспользуемся тем, что в этой

процедуре  есть  команда   HALT.

Если вспомнить, то, встретив эту

команду, процессор как бы приос-

танавливается  и  ждет   прихода

сигнала прерывания на вход  INT,

а  потом  уходит  на  исполнение

программы прерывания. В СПЕКТРУ-

МЕ импульсы прерывания следуют с 

частотой 50 Герц, и 50 раз в се-

кунду опрашивается клавиатура по 

прерыванию 1.  Мы  же, установив 

ПРЕРЫВАНИЕ 2,  перехватим  выход

из этой  процедуры на свою прог-

рамму.

   Единственное, не забудем  пе-

ред вызовом процедуры из ПЗУ за-

нести в регистр  'D'  ЕДИНИЦУ, а

на выходе, в прерывающей  проце-

дуре, продвинуть указатель СТЕКА

вверх по ОЗУ на 3 СЛОВА  (напри-

мер, три раза  дать команду 'POP

AF').

140.

; Программа вызова процедуры опроса порта #1F...


START LD A,#FD       ; занесем вектор прерывания,

                     ; равный,

      LD I,A         ; например, #FD (полный адрес

                     ; равен #FDFF)

      LD HL,VARIABLE ; адрес прерывающей процедуры

      LD (#FDFF),HL  ; занесем в адрес вектора

                     ; прерывания адрес программы

      LD D,1         ; регистр должен=1 для того,

                     ; чтобы программа в ПЗУ отра-

                     ; ботала ОДИН раз

      LD IX,#2D3D    ; адрес подпрограммы ТРДОС

                     ; (для версии ТРДОС 5.01 !)

      EI             ; прерывания разрешить

      HALT           ; приостановить процессор и

      IM 2           ; включить прерывание ДВА

      CALL TRDOS     ; вызвать программу из ТРДОС с

                     ; последующей отработкой прог-

                     ; раммы прерывания

      DI             ; запрет прерываниям

      IM 1           ; прерывание ОДИН

      EI             ; разрешить прерывания

      RET            ; выйти из программы


;- - - - - - - прерывающая программа - - - - -


VARIABLE  DI      ; запретим прерывание

          POP HL  ; извлечем из стека адрес

                  ; возврата на

          POP HL  ; программу в ПЗУ после HALT,

          POP HL  ; а также еще два слова

          IM 1    ; прерывание в 1

          RET     ; вернуться


;- - - - - - - - - - - -

TRDOS     PUSH IX  ; вызов программы из ПЗУ ТРДОС

          JP #3D2F ;

;- - - - - - - - - - - -

2

   После  запуска  программы   с

метки START она отработает, и мы 

получим на выходе в регистре 'A' 

НОЛЬ - если все в порядке  (пре-

рывающая  процедура  не включит-

ся).

   Если же Регистр Состояния со-

держал установленные биты, то  в

этом случае включится наша  пре-

рывающая программа, и  в  регис-

тре 'A' мы  получим  байт, кото-

рый можно в дальнейшем проанали-

зировать любым способом.

   Единственным недостатком  та-

кого метода является  резервиро-

вание ячеек ОЗУ под вектор - где

содержится  адрес    прерывающей

программы (в  нашем  случае  это

ячейки ОЗУ с  адресами  #FDFF  и

#FE00), а также  требование сох-

ранять 'старый' регистр 'I', ес-

ли переделываемая программа  ра-

ботала с прерыванием 2.

   Так как младший байт  вектора

прерывания всегда равен  #FF, то

таких адресов в  адресном  прос-

транстве компьютера насчитывает-

ся всего 255. Если исключить ад-

реса, падающие на ПЗУ (а их 63),

то нам останется 255  минус  63,

всего 192 произвольных адреса. А

это достаточно много.

   В ПЗУ ТРДОС есть еще несколь-

ко подпрограмм опроса порта #1F,

которые можно вызвать  таким  же

способом.  Одна из них находится

по адресу #3DAB < #3DB5 >.140.


#3DAB  < #3DB5 >  IN A,(#1F) ; прочитаем порт

                  AND 02     ; выделим бит 'ЗАПРОС

                             ; ДАННЫХ'

                             ; при операциях чтения

                             ; -записи

                             ; или бит 'ИНДЕКС'

                             ; при остальных опера-

                             ; циях


                 LD B,A      ; спрячем полученное

                             ; значение

          LOOP1  IN A,(#1F)  ; еще раз прочитаем

                             ; порт

                 AND 02      ; выделим бит

                 CP B        ; сравним с прежним

                 RET NZ      ; если бит установлен

                             ; - выйдем

                 INC DE      ; увеличим значение

                 LD A,E      ; если попытки не

                             ; кончились,

                 OR A        ;

                 JR NZ,LOOP1 ; то повторить чтение

                             ; порта

2

   Эту подпрограмму удобно вызы-

вать в случаях:

 - когда надо засинхронизировать

выполнение программы на  прохож-

дение Индексного отверстия, т.е.

на  новый оборот диска, или  уз-

нать, вставлен ли диск  и  готов

ли он?

 - при операциях чтения/записи -

ожидая сигнала ЗАПРОС ДАННЫХ.

   Как мы видим, в этой подпрог-

рамме отсутствует команда  HALT,

по  которой  ВТОРЫМ  прерыванием

точно  перехватывается  управле-

ние на  программу  пользователя.

Но в любом случае при  появлении

прерывающего импульса  INT  про-

цессор будет  отрабатывать  цикл

LOOP1, и перехват все равно сос-

тоится, а в регистре 'B'  или  в

'A' будет нужный нам байт с  вы-

деленным ВТОРЫМ битом.

   Как и  в  предыдущем  случае,

сначала надо  установить  Вектор

прерывания, а в прерывающей про-

грамме   передвинуть   Указатель

стека на ДВА байта вверх  (чтобы

вернуться не в ПЗУ, а в програм-

му пользователя). Перед  вызовом

подпрограммы ПЗУ в регистры 'DE' 

необходимо занести 00 для макси-

мального цикла опроса. Ну, и ес-

тественно,  дать  команды  EI  и

IM2.

140.

; Программа вызова процедуры  #3DAB < #3DB5 >


start  LD A,VECTOR             ; установим вектор

                               ; прерывания

       LD I,A

       LD HL,VARIABLE          ; занесем по адресу

                               ; вектора

       LD (VECTOR *256+255),HL ; адрес прерывающей

                               ; программы

       EI

       HALT

       LD DE,00       ; число попыток

       LD IX,#3DAB    ; программа в ПЗУ (для v.5.01)

       IM 2           ; второе прерывание

       CALL TRDOS     ; исполнить программу В ПЗУ и

                      ; прерывающую подпрограмму

       IM 1           ;

       RET            ; вернуться, в регистре 'A' и

                      ; 'B' будет значение из

                      ; порта #1F

;- - - - - - - - - -

TRDOS         PUSH IX

              JP #3D2F

;- - - - - - - - - -

VARIABLE     DI

             POP HL

             RET

;- - - - - - - - - -

2

   Для полноты  изложения  можно

еще привести адрес  подпрограммы

опроса порта  #1F  с  выделением

ЧЕТВЕРТОГО бита "ПОТЕРЯ ДАННЫХ",

если была операция  ЧТЕНИЯ/ЗАПИ-

СИ.  При  остальных  операциях -

бит "ГОЛОВКА в  ИСХОДНОМ СОСТОЯ-

НИИ".

   Вызывают  эту    подпрограмму

ОБЫЧНЫМ  способом,  без   всяких

ухищрений.  Единственное, в  ре-

гистр 'C' перед вызовом надо за-

нести число 1  -  для  отработки

подпрограммы всего один раз.

140.

;... процедура чтения порта #1F с выделением бита 4

;... перед вызовом дать комманду LD C,1

#3E30  < #3E3A >   IN A,(#1F) ; опросить порт

                   AND 04     ; выделить бит 4

                   RET NZ     ; если он установлен

                              ; - выйти

                   INC B      ; увеличить 'B'

                   DEC C      ; уменьшить 'C'

                   RET Z      ; если 'C'=0, то выйти

                   ...

2

   Теперь,  когда  мы  научились

опрашивать порт  Состояния,  нам

надо решить, что же  должна  де-

лать  программа  пользователя  в

случае установки какого-либо би-

та в этом порту, т.е. при  ошиб-

ке операций.


При операциях ЧТЕНИЯ/ЗАПИСИ:

 - Установлен

 2-й бит - 'ПОТЕРЯ ДАННЫХ'

       и/или

 3-й бит - 'ОШИБКА КОНТРОЛЬНОГО

           КОДА'

       и/или

 4-й бит - 'МАССИВ НЕ НАЙДЕН'

       и/или

 5-й бит - 'ОШИБКА ЗАПИСИ'


   В этом случае Вам  необходимо

повторить операцию  определенное

количество раз, и если  бит  все

так же будет установлен, то  это

значит, что у Вас сбойный  диск.

Обрываете   операцию    командой

'ПРИНУДИТЕЛЬНОЕ  ПРЕРЫВАНИЕ',  а

дальше поступаете так, как  счи-

таете нужным.


- Установлен  6-й  бит - 'ЗАЩИТА

ЗАПИСИ' (т.е. диск защищен).


   В этом случае также прерывае-

те  операцию  подачей    команды

'ПРЕРЫВАНИЕ' и  любезно  просите

снять  защиту  с  диска, или  не

просите...


- Сброшен 7-й  бит - 'ГОТОВНОСТЬ

ДИСКОВОДА'


   А в этом случае Ваша програм-

ма должна просто  подождать  го-

товности  дисковода  пустым цик-

лом. А теперь  давайте продолжим  

и посмотрим, что же еще полезно-

го есть в ПЗУ ТРДОС.


  Процедура определения НОМЕРА

     ЦИЛИНДРА под головками.

     ~~~~~~~~~~~~~~~~~~~~~~~

     Адрес #1DFA  < #1E36 >.


   После вызова этой подпрограм-

мы в регистре  'A'  возвращается

НОМЕР ЦИЛИНДРА;  этот  же  номер

сразу заносится в Регистр Дорож-

ки контроллера.  Также при своей

работе  подпрограмма  опрашивает

клавишу 'BREAK' = C/SH+SPACE   и

печатает сообщение, если клавиша

нажата.

   Эта подпрограмма является за-

конченной, т.е. не  нуждается  в

подготовительных  действиях.   К

сожалению, при ее работе  затра-

гивается ряд адресов в ОЗУ  Сис-

темных переменных.


Условия вызова:

- в регистр IY занести  значение

  #5C3A;

- в  регистре  'B'  должен  быть

  НОЛЬ;

- в адресе ОЗУ #5C3A должно быть

  значение #FF.


  Купируются, т.е. изменяются  в

  процессе  работы  подпрограммы

  адреса ОЗУ #5D16 и #5CCD.



Процедура 'Поиск нужной ДОРОЖКИ'

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      Адрес #3E59 <#3E63>.


   При работе подпрограмма нахо-

дит на диске нужную ДОРОЖКУ (вы-

ходит на нужный ЦИЛИНДР и прижи-

мает ВЕРХНЮЮ или НИЖНЮЮ  головку

исходя  из   заданного    номера

ДОРОЖКИ). Также  опрашивает кла-

вишу 'BREAK' = C/SH+SPACE.  Под-

программа является законченной и

работает даже на неформатирован-

ном диске.


Условия вызова:

- в регистр IY занести  значение

  #5C3A;

- в  регистре  'A'  должен  быть

  НОМЕР требуемой ДОРОЖКИ. (Если

  номер 0 или четный, то дорожка

  верхняя, если нечетный -  ниж-

  няя);

- в адресе ОЗУ #5C3A должен быть

  байт #FF;

- в адресах #5CF6 И #5CF7 должны

  быть НУЛИ;

- в  адресе  #5CC8  должно  быть

  значение #83;

- в адрес  #5CFA  занести 08 или

  00.


  Купируется, т.е.  уничтожается

  содержимое ячеек памяти по ад-

  ресам #5D16 и #5CCD.



Процедура 'ПОИСК нужной ДОРОЖКИ'

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  Адрес вызова #2EF0 < #2F3A >


   Это вторая процедура  ПОИСКА.

Она более приемлема  в  програм-

мах с дефицитом места в  памяти.

Во время своей работы  процедура

портит всего  лишь  одну  ячейку

памяти по адресу #5C00. При  ра-

боте  подпрограмма  находит   на

диске нужную ДОРОЖКУ (выходит на

нужный ЦИЛИНДР  и прижимает ВЕР-

ХНЮЮ или  НИЖНЮЮ головку, исходя

из  заданного  номера  ДОРОЖКИ).

Подпрограмма  является закончен-

ной  и работает даже на неотфор-

матированном диске.


Условия вызова:

- в адресе ОЗУ #5C00 должен быть

  НОЛЬ;

- в регистр  'C' заносится номер

  ДОРОЖКИ.


В эту программу можно войти  еще

по адресу:

        #2EFB < #2F45 >

В этом случае ячейка  ОЗУ  #5C00

не затрагивается, но перед вызо-

вом  подпрограммы  необходимо  в

порт  #FF  подать  БАЙТ  'ГОТОВ-

НОСТЬ, ВЕРХНЯЯ  СТОРОНА ДИСКА  и

т.д.', а в регистр 'C' перед вы-

зовом занести Номер ДОРОЖКИ.

   Если  кого-то  не  устраивают

данные методы поиска ДОРОЖКИ  на

диске, то можно написать  проце-

дуру 'ПОИСК  ДОРОЖКИ', основыва-

ясь на команде микросхемы 'ШАГ'.

   Правда,  это  будет  занимать

больше места в ОЗУ, и по быстро-

действию она будет намного  мед-

леннее всех подпрограмм ПОИСКА в

ПЗУ ТРДОС.

   А выглядит это приблизительно

так:

- подать команду 'ВОСТАНОВЛЕНИЕ'

- подать в порт #FF биты  ГОТОВ-

  НОСТИ  и  ВЕРХНЕЙ  ПОВЕРХНОСТИ

  (и др.);

- занести в регистр (например, в

  'C') НОМЕР ДОРОЖКИ;

- скорректировать номер  дорожки

  в НОМЕР ЦИЛИНДРА  И  в  повер-

  хность диска и давать  команду

  контроллеру  'ШАГ  ВПЕРЕД'  (с

  установленными  модификаторами

  'головку прижать, изменять ре-

  гистр  дорожки')    полученное

  число раз...

140.

; Подпрограмма поиска дорожки командой 'ШАГ'.


     ld c,#20    ; номер ДОРОЖКИ


     ld a,0      ; КОМАНДА ВОССТАНОВЛЕНИЕ

     call trdos  ; ЗАНЕСТИ В ПОРТ #1F


     ld a,#3C    ; готовность, ВЕРХ

     call trdos  ; занести в регистр #FF


     ld a,c      ; сдублировать номер дорожки

     or a        ; сбросить регистр флагов

     rra         ; разделить Номер дорожки на 2

                 ; в 'A' теперь номер ЦИЛИНДРА

     ld b,a      ; поместить в счетчик цикла

     jr nc ,STEP ; число 'Номер Дорожки' было

                 ; четное ? Если да - переход.


     ld a,#2C    ; нет - значит поверхность НИЗ

     call trdos  ; занести в порт #FF

STEP ld a,#5b    ; команда 'ШАГ ВПЕРЕД'

     call trdos  ; исполнить команду

     djnz  STEP  ; повторять 'B' чило раз.

                  ..........


; Метка "trdos" в этом случае является подпрограммой

; вызова процедур ТРДОС, которые напрямую работают с

; портами через точку #3D2F.

2

   Как мы видим  из примера, та-

кой  метод  довольмо  неудобен и

громоздок...



Процедура'ЗАПИСЬ ОДНОГО СЕКТОРА'

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      Адрес #3F00 < #3F0A>


  Процедура записывает один сек-

тор на текущую ДОРОЖКУ (на кото-

рой стоят  головки  дисковода) и

выбранную поверхность.


Условия вызова:

- головка дисковода должна  быть

  выведена на нужную  дорожку  и

  прижата к диску;

- в адресах  #5D00/#5D01  должен

  быть помещен адрес ОЗУ, из ко-

  торого будут записываться  256

  байтов в сектор;

- в адрес #5CFF должен быть  по-

  мещен  ЛОГИЧЕСКИЙ НОМЕР СЕКТО-

  РА, (т.е.  нумерация  секторов

  начинается  с  0, подпрограмма

  потом скорректирует этот номер

  до физического).

- сохранить  данные  из   адреса

  #5CFE, так как этот адрес  при

  работе  купируется,  т.е.  ис-

  пользуется при работе.


  !!! Самым, пожалуй, сложным  в

условии вызова процедуры являет-

ся  условие  ПРИЖАТЬ  ГОЛОВКИ  К

ДИСКУ. Решается это простыми ме-

тодами. Так как  запись  сектора

является  как  бы   продолжением

действия программы  пользователя

в цепочке  действий,  то  данная

процедура  должна  следовать  за

процедурой ПОИСК НУЖНОЙ ДОРОЖКИ.

   А так  как  'ПОИСК'  является

командой,  которая  и  прижимает

головки к диску, вызвав програм-

му 'ЗАПИСЬ' сразу же после окон-

чания действия 'ПОИСКА', мы  без

труда выполним нужное условие.

   Если же в программе пользова-

теля вызов процедуры 'ПОИСК' от-

стоит от вызова 'ЗАПИСИ'  далеко

по времени, и программа не успе-

вает подать команду 'ЗАПИСЬ'  на

прижатые  головки,  то,   вызвав

повторно подпрограмму 'ПОИСК'  с

теми же параметрами, мы загрузим

(прижмем) головки.  Можно  также

дать команду дисководу  'ШАГ НА-

ЗАД' и следом 'ШАГ ВПЕРЕД' с мо-

дификаторами  кода  'ПРИЖАТЬ ГО-

ЛОВКИ'.



Процедура'ЧТЕНИЕ одного СЕКТОРА'

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      Адрес #2ED1 <#2F1B>


Условия вызова:

- в регистре HL должен быть  ад-

  рес  ОЗУ,  куда  будет  считы-

  ваться сектор;

- в регистре 'E' должен быть но-

  мер СЕКТОРА (номер ЛОГИЧЕСКИЙ,

  т.е. нумерация с НУЛЯ).


   Подпрограмма вызывается  пос-

ле вывода головок на нужный  ци-

линдр и после прижатия головок к

нужной поверхности.

   Подпрограмма в  своей  работе

опрашивает порт СОСТОЯНИЯ  (#1F)

и повторяет свои действия,  если

был сбой в чтении.



Процедура'ЧТЕНИЕ ОДНОГО СЕКТОРА'

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       Адрес #3F04 <#3F0E>


   Это вторая подпрограмма  чте-

ния сектора.


Условия вызова:

- головка дисковода должна  быть

  выведена на нужную  дорожку  и

  прижата к диску;

- в адресах  #5D00/#5D01  должен

  быть помещен адрес ОЗУ, в  ко-

  торый  будут  считываться  256

  байтов из сектора;

- в адрес #5CFF должен быть  по-

  мещен ЛОГИЧЕСКИЙ НОМЕР СЕКТОРА

  (т.е. нумерация секторов начи-

  нается с 0,  подпрограмма  по-

  том скорректирует  этот  номер

  до физического);

- Сохранить  данные  из   адреса

  #5CFE, так как этот адрес  при

  работе  купируется,  т.е.  ис-

  пользуется при работе.



Процедура 'Подача команд ПЕРВОГО

типа с ожиданием их исполнения'.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      Адрес #2F0D <#2F57 >


   Эту подпрограмму удобно вызы-

вать, когда требуется  исполнить

команды  'ВОСТАНОВЛЕНИЕ', 'ШАГ',

'ПОИСК'.

   Подпрограмма при своей  рабо-

те записывает код команды в порт

#1F и затем, опрашивая порт #FF,

ждет, когда  эта  команда испол-

нится.


Условия вызова:

- в регистре 'A' должен быть код

команды.



 Процедура 'ЦИКЛИЧЕСКАЯ ЗАПИСЬ

            В ПОРТ'.

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       Адрес #2075 <#20B1>


   Обычно эта  подпрограмма  ис-

пользуется в процессе ФОРМАТИРО-

ВАНИЯ  ДОРОЖКИ.  Но ее можно ис-

пользовать для нестандартной за-

писи информации на дорожку  (на-

пример,  создание   юстировочной

дорожки, или для полного уничто-

жения информации на дорожке).  А

если исхитриться, то можно запи-

сывать информацию и в СЕКТОР.


Условия вызова:

- головки должны  быть  выведены

  на нужный цилиндр и прижаты  к

  нужной поверхности диска;

- Подать команду  'ЗАПИСЬ'  (до-

  рожки или сектора). Если запи-

  сывается СЕКТОР, то в  регистр

  СЕКТОРА поместить номер секто-

  ра;

- В  регистре  'D'  должен  быть

  байт для записи"

- В  регистре  'B'  должно  быть

  число:  сколько  раз  записать

  байт из регистра 'D';

- В регистре 'C' должно быть #7F

  (порт данных).



Процедура "ФОРМАТИРОВАНИЕ  ОДНОЙ

            ДОРОЖКИ".

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       Адрес #1FC1 <#1FFD>


   Подпрограмма форматирует одну

дорожку   стандартным  способом.

Опрашивает  порт состояния (#1F)

и выдает надпись на экран,  если

диск защищен от записи.


Условия вызова:

- головки должны  быть  выведены

  на нужный цилиндр и прижаты  к

  нужной стороне;

- В ЯЧЕЙКЕ ОЗУ с  адресом  #5CD8

  должно быть значение, отличное

  от НУЛЯ;

- В регистр 'E'  необходимо  по-

  местить номер ЦИЛИНДРА  (от  0

  до 79), на котором  стоят  го-

  ловки. Если  у  Вас  позволяет

  дисковод, то,  выведя  головки

  на цилиндр, больший чем 79, Вы

  можете разметить 80, и 81,и 82

  и т.д. цилиндры  до  тех  пор,

  пока у Вас головки не  упрутся

  в ограничитель.


   Если же  Вы хотите отформати-

ровать нестандартно  -  пожалуй-

ста. Вы  можете,  например,  1-й

трек отформатировать  с  систем-

ным номером 255, но  работать  с

таким диском в ТРДОС СТАНДАРТНЫ-

МИ командами будет трудновато!!!


   Для  форматирования   дорожки

нестандартным образом можно  ис-

пользовать точку входа этой под-

программы:

          #1FC9 <#2005 >


   В  этом  случае,  кроме  всех

предыдущих условий, необходимо:


- в регистре  'HL'  должен  быть

  помещен  адрес  дампа  данных,

  где последовательно  размещены

  номера  СЕКТОРОВ  диска   (для

  нормального  выхода  ПОСЛЕДНИМ

  НОМЕРОМ в  дампе  должен  быть

  номер #10!).  ТРДОС использует

  адрес  дампа  данных  в ПЗУ по

  адресу #1F7D <#1FB9>;

- в  порт  КОМАНД  (#1F)  должна

  быть занесена команда ФОРМАТИ-

  РОВАНИЕ (например, байт #F4).


   Подробнее о процессе ФОРМАТИ-

РОВАНИЯ  будет  рассказано  чуть

позднее.



   Процедуры 'ЦИКЛ ЗАДЕРЖКИ'.

   ~~~~~~~~~~~~~~~~~~~~~~~~~~

    Адрес 1.  #3DF3 <#3DFE>

    Адрес 2.  #3E96 <#3EA0>


   Это подпрограмма задержки для

ожидания исполнения команд  дис-

ководом. Иногда ее полезно вызы-

вать,  чтобы  быть  уверенным  в

том,  что  последующая   команда

дисководу поступит  на него тог-

да, когда он свободен.

   Первая  подпрограмма:  задер-

жка  составляет   приблизительно

0.3 секунды, а вторая задержка -

приблизительно 1.2 секунды.



  Процедура 'ПЕРЕСЫЛКА ДАМПА'.

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Адрес 1.  #17DD <#180D>

    Адрес 2.  #28A5 <#2FEB>


   По этому адресу в  ПЗУ  ТРДОС

находятся команды:


              LDIR

              RET


   Вызов этой  подпрограммы  бы-

вает полезен тогда,  когда  надо

заморочить голову  хаккерам  или

для перекачки информации из  ПЗУ

ТРДОС в ОЗУ: например, части ка-

кой-либо процедуры.

   Условия вызова такие же,  как

и обыкновенной команды 'LDIR'.

   Можно было  бы  привести  еще

множество  полезных  подпрограмм

из ПЗУ.

   Но все вышеизложенное  доста-

точно для того,  чтобы  написать

какую угодно программу работы  с

диском, и мы  ограничимся  этими

подпрограммами, которые  являют-

ся главенствующими.

   В  процессе  написания  своих

программ или при разборе  других

аналогичных программ  Вы  можете

обнаружить,  что   необязательно

вызывать подпрограммы  ТРДОС  по

указанным выше адресам. В  конце

этой главы, в приложении,  будут

приведены листинги наиболее важ-

ных подпрограмм ПЗУ ТРДОС с ука-

занием адресов, и вам самим  ре-

шать, по какому адресу  вызывать

ту или иную процедуру.




                         TR DOS  ДЛЯ НАЧИНАЮЩИХ



                              ГЛАВА 2


                      ВНУТРЕННЯЯ ОРГАНИЗАЦИЯ ДИСКА

                     ------------------------------


        Как вы уже знаете, существует множество систем, форматов и способов 

записи на диски информации в разных компьютерах.

        В системе ТР ДОС Спектрума основной стандарт разметки дисков таков:

256 БАЙТОВ НА 1 СЕКТОР;

16  СЕКТОРОВ НА ДОРОЖКЕ.


        Как уже было сказано, в ТР ДОС используются диски DD/DS (2D/2S).

Так вот, эти диски ТР ДОС форматирует таким образом:  

160 ДОРОЖЕК НА ВСЕМ ДИСКЕ, по 80  ДОРОЖЕК НА КАЖДОЙ СТОРОНЕ. Дорожки нумеруются

от 0 до 159.

        Нулевая  дорожка  расположена у внешнего края диска - на верхней стороне

поверхности 0. Далее - 1-я дорожка на нижней стороне диска.

        Две  дорожки, расположенные на разных поверхностях, но находящиеся одна 

над другой, называют ЦИЛИНДРОМ. На каждой дорожке расположено по 16 СЕКТОРОВ, 

которые нумеруются с 1 по 16.

        Физически сектора на диске располагаются не по порядку  возрастания 

номеров , а с пропуском в один сектор  (см.  РИС.1).

        Это  было сделано, видимо, из опасения, что процессор, обработав один 

сектор, не успеет приготовиться и сразу считать следующий сектор. Пришлось бы 

ждать еще один  оборот диска для того, чтобы попасть на следующий по порядку 

сектор.

        А так, пока обрабатывается сектор номер 1, диск прокрутится, пройдет 

сектор 9, а когда будет все готово, головка дисковода как раз попадет на 

сектор  2.

        Такая разметка диска присуща самой системе ТРДОС, но это не значит, что

нельзя разметить по другому. Дело  в  том, что системе безразлично, вообще-то,

как  на диске располагаются сектора в дорожке. Самое главное, чтобы на данной 

дорожке были сектора с  нужными  номерами, нужной размерности и нужным числом.

        Вот г-н Родионов подумал и написал программу DCU 2, где  сектора  

расположены  по порядку возрастания номеров, не побоялся, что система не успеет

обработать два идущих подряд сектора - и не ошибся! Получился так называемый 

"быстрый FAST диск", где информация с диска обрабатывается чуть быстрее, чем

на стандартно размеченном диске (см. РИС 2).


        Да, и самое главное- г. Родионов не побоялся разметить диск на число

дорожек больше, чем 160, и тоже не прогадал, выжав из диска еще дополнительные 

килобайты.


        Вообще-то можно разметить диск на еще большее число дорожек, но чем 

головка ближе к центру диска, тем  риск ошибки при считывании информации 

возрастает. И уж совсем плохо будет, если Вам придет в голову  разметить  диск 

с  двухсотой дорожкой. Ваш дисковод будет безуспешно долбиться головкой об

ограничители и Вам станет не по себе от дикого звука, который при этом 

будет раздаваться.


        Это все хорошо, ну а как все таки система узнает где и в каком месте  

диска она находится?  Дело в том, что у каждого  сектора находится служебная 

область, где записана вся необходимая информация.

        В начале у сектора идет область синхронизации, состоящая  из  кодов 

#4Е и нулей - эта так называемая область  "пробела", нужная для синхронизации 

микросхемы ВГ93  на область данных ( см. РИС 3 ). Далее  идет индексная метка и

область И.А.М. (индексная адресная метка). Вот в ней то и записана вся 

информация о данном секторе. И.А.М. начинается с байтов #A1,#FE. Далее следуют:

НОМЕР ЦИЛИНДРА    ( начало с 0  )

СТОРОНА ДИСКА     (0 верх ,1 низ)

НОМЕР СЕКТОРА     ( с 1 по 16   )

ТИП   СЕКТОРА     (0 - сектор  128 БАЙТ

(его длина)        1 - сектор  256 БАЙТ

                   2 - сектор  512 БАЙТ

                   3 - сектор 1024 БАЙТ

КОНТРОЛЬНАЯ СУММА ( циклическая сумма )


        Если система не находит область ИАМ или контрольная сумма не 

совпадает с прочитанной (а контрольная сумма просчитывается микросхемой 

каждый раз), то выдается код ОШИБКИ, и обработка или повторяется, или  

прекращается в зависимости от числа попыток, заложенных программистом в 

подпрограмму обработки ошибок.

        Если все в порядке, то за областью ИАМ следуют байты пробела, коды 

#4Е и нули. За ними идет АДРЕСНАЯ МЕТКА ДАННЫХ - коды #A1 ; #FB  и только тогда

идут собственно те данные, которые считываются или записываются на диск 

пользователем.

        Эта область, когда данных там нет, содержит 256 нулей, (в случае ТР 

ДОС). После области данных следует код КОНТРОЛЬНОЙ СУММЫ этой области. И вот 

так в каждом секторе.

        Вся эта информация заносится в процессе форматирования диска и, в 

конечном итоге, зависит от программиста. Можно разметить по своему усмотрению 

все сектора на  дорожке с одним номером, или дорожки с обратной нумерацией, или

еще чего придумать, но тогда придется  писать уж и программу, которая  будет

разбираться на диске со всей этой мешаниной, а это будет уже не ТР ДОС, а 

что-то другое.

        ТР ДОС в этом не разберется и с полоборота выдаст:

                 * DISK ERROR * !


        Для примера приводим таблицу всех байтов, которые находятся в одном 

секторе, в служебной области (см. ТАБЛ.1).


        Вся эта информация заносится за один проход, по всем секторам на одной 

дорожке во время форматирования.

        Собственно, это и есть процесс форматирования. Из этого всего следует, 

что если у Вас возникнет желание отформатировать одну дорожку - на здоровье,

но отдельно отформатировать или переформатировать один сектор нельзя! Только 

целиком дорожку!


        Если мы внимательно подсчитаем, сколько все же килобайт  помещается на 

одной дорожке со всеми метками, "пробелами", и т.д,  то окажется - на дорожке

приблизительно 6 200 байтов.

*  4 килобайта информации, которой располагает

     пользователь;

*  2 килобайта всех служебных меток.

        Да, за все надо платить. Без этих 2-х килобайт меток обмен с диском был

бы крайне затруднителен, если возможен вообще!

        Итак, у нас есть размеченный диск :

160 ДОРОЖЕК , 16 СЕКТОРОВ В ДОРОЖКЕ;

ВСЕ НЕЧЕТНЫЕ ДОРОЖКИ РАСПОЛАГАЮТСЯ НА НИЖНЕЙ СТОРОНЕ;

НУЛЕВАЯ И ВСЕ ЧЕТНЫЕ НА - ВЕРХНЕЙ СТОРОНЕ.

        А что-же дальше? Даже если у Вас есть размеченный диск, но не заполнен 

сектор номер 8, то ТР ДОС все равно ответит Вам отказом, и диск рассмотрит как 

сбойный.


        В  ВОСЬМОМ ЛОГИЧЕСКОМ секторе располагается информация ТР ДОС. Это тип 

диска, имя диска, количество дорожек и т.д. Ниже дана раскладка всех 

необходимых байтов сектора.


***************

ВОТ ТУТ НЕОБХОДИМО СДЕЛАТЬ ОТСТУПЛЕНИЕ И ДОГО-

ВОРИТЬСЯ ОБ ОДНОЙ ВЕЩИ ВО ИЗБЕЖАНИЕ ПУТАНИЦЫ !!

ДЕЛО В ТОМ ,ЧТО НОМЕРА СЕКТОРОВ РЕЛЬНО НА ДИС-

КЕ ПОМЕЧЕНЫ НОМЕРАМИ ОТ 1 ДО 16 !!

СИСТЕМА  ТР ДОС  НА ВЕРХНЕМ УРОВНЕ ИСПОЛЬЗУЕТ

НУМЕРАЦИЮ  С 0 ДО 15, КОРРЕКТИРУЯ ПОТОМ НОМЕРА

СЕКТОРОВ ДО РЕАЛЬНЫХ.

БУДЕМ СЧИТАТЬ, ЧТО НОМЕРА С 1 ДО 16 БУДУТ

ФИЗИЧЕСКИМИ НОМЕРАМИ СЕКТОРА,

А НОМЕРА С 0 ДО 15   -    ЛОГИЧЕСКИМИ.

В КАЖДОМ КОНКРЕТНОМ СЛУЧАЕ МЫ БУДЕМ УКАЗЫВАТЬ

ГДЕ ЛОГИЧЕСКАЯ НУМЕРАЦИЯ, А ГДЕ ФИЗИЧЕСКАЯ .

**************



        СОДЕРЖАНИЕ ВОСЬМОГО (лог.) СЕКТОРА.

        ----------------------------------


        С нулевого по 224 байт в секторе записаны нули.


НОМЕР БАЙТА               НАЗНАЧЕНИЕ

-------------------------------------------------

225      номер  первого свободного сектора

226      номер  первой свободной дорожки

227      диск   двусторонний /односторонний

                = #16 =        = #18 =

228      количество    файлов     на диске

229      количество     свободных секторов

                      МЛ.БАЙТ

230                   СТ.БАЙТ

231      количество секторов в дорожке = #10

232                      00

233                      00

234 по 242              #20

243                      00

244      количество удаленных файлов

245 по 252    ИМЯ  ДИСКА   ( 8 символов )

------------------------------------------------


        Самым  главным байтом на секторе является байт с номером  231 !

        Можете стереть или изменить всю информацию на секторе - диск еще 

останется работоспособным, но если Вы измените этот байт - диск окажется 

сбойным и прочитать его можно будет только каким-нибудь Диск-Доктором, загрузив

в него целиком дорожку N 0.


        Востановить же диск после таких манипуляций можно... ,тем же самым 

"Доктором", записав на это место код #10.

        Сектора  с  0  ПО  7 (лог.номера) занимает каталог диска.

        Пока  на диске нет файлов, там записаны нули, но как только на диске 

появляются файлы, система автоматически создает на каждый файл нечто вроде 

описателя файла, т.е. создает каталог.

        На каждый описатель - заголовок файла резервируется 16 байт. Таким 

образом, на диске может быть записано  не больше (256 байт сектора)/ (16 байт 

заголовка) * (8 секторов) = 128 ФАЙЛОВ !!! Лишние файлы будут просто не 

создаваться на диске.

        Кстати, и название свое система ТР ДОС-128 получила именно в честь 

этого числа.

        Каждый байт заголовка имеет свое значение для системы.

        Структура заголовка файла имеет следующий вид :


БАЙТЫ       !          НАЗНАЧЕНИЕ

------------!-----------------------------

0....7      ! ИМЯ ФАЙЛА ( 8 символов )

8           ! ТИП ФАЙЛА (  B,C,D,#   )

9           ! НАЧАЛО ФАЙЛА ( мл.байт) В ОЗУ

10          ! НАЧАЛО ФАЙЛА ( ст.байт) В ОЗУ

11          ! ДЛИНА ФАЙЛА  (мл.байт)

12          ! ДЛИНА ФАЙЛА  (ст.байт)

13          ! ЧИСЛО ЗАНИМАЕНЫХ СЕКТОРОВ

14          ! НОМЕР ПЕРВОГО СЕКТОРА ФАЙЛА

15          ! НОМЕР ДОРОЖКИ НАЧАЛА ФАЙЛА

-------------------------------------------

*    Имя файла может состоять вообще-то из любых символов, стрингов, но 

рекомендуется давать все-таки имена, состоящие из кодов не меньше 30 и не 

больше 127.

        Можно, конечно, составить имя и из кодов, не входящих в данный диапазон,

но вероятность того, что Вы нарветесь на такую ситуацию, когда после команды

" САТ "  Ваш компьютер напишет - INVALID COLOR, DISK ERROR и т.д. и т.п. - 

ОЧЕНЬ ВЕЛИКА !

        Так что не искушайте судьбу , да и работать с простыми именами проще.


*      Тип файла.

Системой воспринимаются только файлы с типом B  

В       файл БЕЙСИКА

C       файл кодов

D       файл  типа ДАННЫЕ

#       файл произвольно-последова-

        тельного типа.

Но это не значит, что нельзя работать с другими типами файлов.

        Например, Вам захотелось, чтобы программа в процессе работы загружала 

или сохраняла файлы состояния ситуации с типом, отличным от системных.

Отчего  же нельзя, например , дать при выгрузке тип файла < Z >?  Можно, но 

только нужно помнить, что работать с этим файлом из системы ТР ДОС стандартными

командами Вы не сможете!  Это можно сделать только АССЕМБЛЕРОМ.


*     Начало файла в ОЗУ  ( СТАРТ ).

Это  справедливо  только по отношению к файлам типа КОДЫ , файл БЕЙСИКА  имеет 

в этих байтах параметр 'ДЛИНА ПРОГРАММЫ'.


        Вообще-то, если разобраться, то этот параметр нужен только в том случае,

когда Вы даете команду : LOAD "filename" CODE без  указания - КУДА загружать 

коды, по какому адресу. Но если Вы даете полное указание куда загрузить файл с 

диска, то этот параметр становится не очень то и нужным.


*      ДЛИНА ФАЙЛА  В БАЙТАХ

Реальная длина файла, то есть сколько байтов он занимает в ОЗУ. Так как 

перекачка файла с диска происходит  через специальный системный буфер ТР ДОС,

то эта величина указывает ТР ДОС сколько конкретно байтов из сектора будет 

пересылаться по нужному адресу через буфер в ОЗУ.


*      КОЛИЧЕСТВО ЗАНИМАЕМЫХ СЕКТОРОВ.

Важная величина, по которой и происходит, в конечном итоге, считывание / запись

файла целиком. Даже если реальная длина файла составляет 1 байт, все равно на  

диске он будет занимать целый сектор 256  БАЙТ.

        ОТСЮДА  ВЫВОД:  желательно, чтобы реальная длина файла при записи была 

кратна 256 - тем самым Вы сохраните место на диске, так как сектора будут 

заняты полностью.

        С  файлами  БЕЙСИКА чуть сложнее, но о них немного позднее...


*      НОМЕР ПЕРВОГО СЕКТОРА ФАЙЛА

Тут все ясно - номер сектора на дорожке с которого начинается кодовая 

последовательность файла. Может принимать значения от 0 до 15 (лог.номер).


*      НОМЕР ПЕРВОГО ТРЕКА ФАЙЛА

Номер дорожки на которой находится начальный сектор кодовой последовательности 

файла. Может иметь номера от 1 до 159.


        Файлы записываются на диск в виде кодовой последовательности, сектор за

сектором. Если на дорожке не хватает секторов, запись переходит на следующую по

порядку  номеров, дорожку.  И так пока вся кодовая последовательность не будет 

записана на диск.

        После  записи  система  обновляет сектор номер 8 и записывает туда 

первый свободный трек, сектор, количество свободных оставшихся секторов.


        Если Вы внимательно прочитали до этого места, то без труда увидите, что

длина файла жестко регламентирована   объемом  в   255   секторов   ( #FF ),

т.е. 255*256=65280  БАЙТОВ.


        Реально , если захотеть и если не пользоваться "переменными"  ТР ДОС, 

то можно соорудить файл хоть на весь  диск. Естественно,  создав такого 

монстра, Вам потребуется создать и свою программу для его считывания и записи.


        Если  происходит стирание файла, то первым делом в каталоге вместо 

первого символа имени файла записывается код 01.


        В  восьмом секторе увеличивается байт "количествостертых  файлов".

        Реально, после  стирания объем  свободных  секторов на  диске  не  

увеличивается (если, конечно, Вы не стираете  самый  последний  файл  в списке-

тогда можно не беспокоится - место освободится).


        Чтобы освободить сектора от "стертого файла", необходимо  перезаписать 

все файлы, расположенные после стертого файла на его место, что и делает 

команда MOVE. После этой команды происходит перезапись всех  файлов, записанных 

после стертого файла, обновляется  информация  в секторе номер 8 , и у Вас

появляется на диске ровно столько свободных секторов, сколько  занимал Ваш файл.

И  расположены эти освобожденные сектора сразу после всех файлов.


        В этом одно из неудобств системы ТР ДОС: чтобы освободить сектора  

стертого файла, записанного в начале диска, Вам приходится перезаписывать всю

информацию на диске, "сдвигая ее вниз".


        Например, в IBM система MS-DOS отслеживает все сектора и при стирании 

файлов сектора освобождаются сразу, так что один файл может быть расположен

в различных секторах, на различных дорожках и необязательно в упорядоченном 

виде.

        При загрузке такого файла (его еще называют сильно сегментированным), 

затрачивается немного больше времени, но система находит по всему диску именно

те сектора, на которых записан загружаемый файл.


        Но зато у ТР ДОС преимущества: ее файлы всегда единое  целое и записаны

всегда последовательно по возрастанию секторов и треков.


        Восстановить файл после процедуры стирания легко - надо найти в 

секторах каталога Ваш файл с 01 вместо первой буквы и записать вместо 01 какой 

нибудь удобоваримый символ, и все !


        Да, еще не забудьте уменьшить байт стертых файлов в секторе номер 8 и 

увеличить байт "количество файлов" на диске.


        Но  !!!  Если  Вы дали команду MOVE, можете не сомневаться - на  месте 

вашего стертого файла будет какой-нибудь другой. И в секторах каталога Вы, 

скорее всего, не обнаружите даже воспоминания об имени Вашего файла.

        Так что НЕ бойтесь стирать, а БОЙТЕСЬ команды MOVE .

        На рисунке 4 представлен дамп первых байтов области  каталога диска. 

Слева представление в шестнадцатиричных  кодах; справа - в символах. На рисунке

5 дан дамп программы типа ' КОДЫ ', в таком виде, в каком он лежит на диске в 

секторе.


       Давайте теперь посмотрим как  лежит на диске в секторе блок файла, 

написанного на БЕЙСИКЕ. Составим  простую программу:

   10 PRINT "PROGRAMM NO AUTO RUN"


Запишем ее на диск командой :

    RANDOMIZE USR 15619:REM:SAVE "PROG1"


В  каком-нибудь  ДИСК-ДОКТОРЕ  найдем  тело  самой программы на диске. Найти 

очень просто: смотрим сначала область каталога, находим имя нашего файла, 

находим в заголовке номер первого сектора файла и номер дорожки. Затем Вам 

нужно будет только ввести эти данные в ДИСК-ДОКТОР, а он уж сам найдет данный 

сектор на данной дорожке.

       Но вот файл на секторе найден, и что же мы видим? (см.РИС.6).


        С первыми четырьмя байтами понятно ; это номер строки БЕЙСИКА - 2 байта и длина всей строки - 2 байта.

Следом  идет текст нашей строки, оканчивающийся кодом  #0D. Следом идет байт #80, сигнализатор конца

всей программы. (На рисунке эти байты выделены инверсией). А дальше идет 

какой-то довесок. Мы  видим  справа в символьном представлениию и имя нашей  

программы и, вроде бы, число 15619, и многое другое.

        Все правильно! Вся  эта информация была необходима ТР ДОС на этапе

работы, когда Вы давали команду на сохранение Вашей Бейсик- программы.  (Если  

бы Вы сохраняли коды, то такой же довесок  был бы в самом последнем секторе    

файла <CODE>.)

        Ведь это просто-напросто область ОЗУ, где набиралась  Ваша  командная  

строка, сохраненная вместе с Вашим файлом.

        Вспомним, если Вы попытаетесь записать на диск всего один байт, то файл

будет длиной в 1 сектор - 256  байт.  Вот и в нашем случае программа была не

кратна  длине 256 и поэтому в файл записалась область, граничащая с нашей 

программой, а это как раз область редактора командной строки или EDIT SPACE.


        Для  эксперимента можете стереть всю область после байта  #80 и записав

на диск, загрузить снова, дав команду LOAD...

        Файл загрузится и никаких изменений в работе не произойдет. Получается,

что этот довесок сектора пропадает ?!  Да, пропадает , и Вы можете использовать

его по своему  усмотрению  - записав туда какую-нибудь информацию, (свое имя, 

например), а можете поместить там подпрограмму, например дешифрации.

        Правда, загружать эту подпрограмму из этого довеска Вам придется 

специальным загрузчиком. Команды ТР ДОС этого сделать не смогут!


        Правда, есть еще два байта в этом "довеске" (будем называть  его  

потерянной  областью файла), которые нужны системе - это байты АВТОЗАПУСКА 

Бейсика.

        На рисунке 7 дамп сектора этой же самой программы, но записанной с 

Функцией LINE 10 , т.е. запуск со строки 10.

        На рисунке мы видим , что в системной области файла после кодовой 

последовательности  #0D,#80,#AA изменился  1  байт  -  вместо 00 стало #0A. Это и есть

сигнал  операционной  системе, что  после  загрузки файл надо сразу запустить 

со строки 10. (Следующий  байт тоже входит в сигнализатор запуска).

Можете стереть (забить 00), все  байты  "довеска". Программа  по  команде LOAD 

самозапускаться не будет.

        Инверсией в символьной части дампа показан "хвостик"  командной  строки,

где видно, что после имени программы  идет функция LINE 10 ; коды #CA,#31,30.


        Все вышеизложенное справедливо и для файлов типа <  DATA  >, с той лишь 

только разницей, что вместо тела программы будет находиться содержимое массива

переменной ( см.РИС 8 ).


        Для  полноты изложения рассмотрим файлы с последовательным  и  

параллельным  доступом , тип <#>. При открытии файла и его записи на диск, 

образуется файл  длиной  в 16 секторов , т.е. в 1 дорожку независимо от того, 

ввели  ли  Вы в запись 1 байт информации или больше.


        Правда, в заголовке  файла, в каталоге, указывается величина в байтах, 

сколько Вы туда записали , но от этого не легче - дорожка на диске уже занята.


        Оба  типа  файла записываются на диск одинаково, и если Вам придет в 

голову последовательный файл открыть для чтения как произвольный и наоборот -

пожалуйста, система  не обидится, но разбираться какие записи в каком порядке 

считывать - в этом случае придется Вам самому.


        С точки зрения программиста, который пишет программы  на  Ассемблере, 

все  эти "довески" и нюансы не столь  важны. В конечном итоге ему нужно считать

/записать  информацию  СЕКТОРА , (секторов), файла , а все остальное - дело 

техники.


        Есть  еще один вид файла, о котором пока не упоминалось  в  этой  

книге. Это так называемые "МАГИК ФАЙЛЫ", т.е. файлы сброшенные ВОЛШЕБНОЙ 

КНОПКОЙ.

        Но  поскольку  этим  файлам  будет посвящена целая глава, то 

останавливаться  здесь  на этой теме мы не будем. Единственное, укажем, что  

эти  файлы  имеют  тип <CODE> и всегда имеют длину файла, равную объему ОЗУ

с  адреса  16384  до 65535. Это слепок всего ОЗУ в файле, с сохраненными 

значениями всех регистров процессора.


        Теперь, когда у нас есть все необходимые сведения о файлах, нам  

необходимо подробнее рассмотреть ту область ОЗУ, которую использует ТР ДОС в 

своей работе, а именно, область системных переменных ТР ДОС.

333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333

     (c) В.Сироткин, г.Краснокаменск.

                         

                              ГЛАВА 3


                     СИСТЕМНЫЕ ПЕРЕМЕННЫЕ ТР ДОС

                   ---------------------------------

      По анлогии с БЕЙСИКОМ, во время своей работы ТР ДОС создает в ОЗУ область 

системных переменных, в которых хранятся параметры настройки диска, команды, 

точки вызовов подпрограмм, флаги состояний и т.д. Единственное различие области 

ТРДОС от системной области БЕЙСИКА заключается в том, что системные переменные  

SOS привязаны к регистру IY процессора. У системных переменных ТР ДОС такой

привязки нет, как нет и своих имен, как это сделано в системных переменных 

БЕЙСИКА.

      Область системных ТР ДОС занимает всего 112 байт в области ОЗУ с адреса 

#5ССВ по адрес #5D3B.

      Область инициализируется при первом же обращении к ТР ДОС (т.е. при 

любой команде, обращенной к диску) и сдвигает область начала БЕЙСИК - программы 

на адрес 23867, оставляя этот адрес активным до сброса компьютера!

      При первом включении компьютера, если он выходит в SOS, область 

системных ТР ДОС не определена, и программа БЕЙСИКА начинается, как обычно, с 

адреса 23755, но при любой команде, обращенной к ТР ДОС, происходит настройка 

адресов системной области ТР ДОС, сдвиг начала программы БЕЙСИКА и 

инсталирование значений переменных операционной системы.

      Во время своей работы, система ТР ДОС может резервировать дополнительный 

буфер для своих нужд размером 257 байт с адреса 23846. Этот буфер является 

динамическим, т.е. создается только на время операций считывания/записи,

сдвигая программу Бейсика вверх по адресам. После операции буфер уничтожается, 

программа Бейсика возвращается на свое место, и все встает на свои места. Эта  

операция происходит незаметно для пользователя и независимо от него, если, 

конечно, он работает стандартным набором команд ТР ДОС.

      Если буфер не может образоваться из-за  низкого RAMTOP (а такая 

ситуация встречается очень часто), то возникает надпись, которая вывела из себя 

многих, кто занимается адаптацией кассетных версий программ на диск, а именно :

                " OUT OF MEMORY".

      В дальнейшем будет показано как решить эту проблему и обходиться без  

этого буфера. Еще больше места требуют для себя команды 'LIST' и 'MOVE';

командe 'MOVE' необходимо аж 4 килобайта, но я думаю, что эта команда должна 

интересовать Вас в последнюю очередь.


      На  Рис. 9 показан дамп ОЗУ системных переменных ТР ДОС после 

инициализации.

      Подробное описание переменных представленно в таблице 2.

      Если Вы в своих разработках будете использовать переменные ТР ДОС, то 

нужно понять одну вещь: если Вы работаете с программой, которая не будет своими 

рабочими кодами затирать эту область, то система сама позаботится обо всех 

переменных сама. Вы, конечно, можете изменять некоторые из переменных или

использовать их значения, но, в данном случае, это не слишком и нужно. Работая 

в такой программе на макроуровне (см. главу "Функции ТР ДОС"), Вы не будете 

испытывать никаких трудностей.

      Но если коды Вашей программы затерли область системных переменных, то 

работать в этой ситуации становится затруднительно, и Вам потребуется 

инициализировать несколько переменных ТР ДОС, чтобы система сработала правильно.

      Вам будет необходимо инсталировать 2-3 переменные + область описателя 

файла.

      Если Вы попытаетесь инициализировать все переменные и работать всеми 

функциями ТР ДОС, то, скорее всего, у Вас ничего не получится из-за отсутствия

свободного места ОЗУ в Вашей программе.

      Если Вам все же удался этот трюк, то что же Вам мешает пойти дальше и, 

инициализировав  переменные БЕЙСИКА, работать в нем, используя стандартные   

команды ТР ДОС ??.

      Короче говоря, если это так, то у Вас достаточно свободного места в ОЗУ, 

и не занимайте, просто-напросто, область системных переменных.

      Хотя некоторые программы составляются именно этим путем, т.е. сохраняют 

всю область переменных ТР ДОС в свободном месте, а во время работы с диском 

пересылают ее на родное место.

      Конечно, можно наворочать в программе ( ради ее секретности и трудности 

"взлома") все ,что угодно, но поверьте - что все тайное всегда становится явным, 

а простота - залог успеха !!


      В главе "Функции ТРДОС" мы рассмотрим вопрос о том, какие именно  

переменные необходимо вводить, чтобы при затертой области системных ТР ДОС 

нормально исполнялись команды чтения/записи на диск. В конечном итоге, ведь это 

нам и надо.

      Забегая вперед, скажу, что составляя программы на самом низком уровне, 

используя отдельные подпрограммы ТР ДОС ПЗУ, можно вообще избавиться от такой

проблемы, как 112 байт системной области.


==================================================

ТАБЛИЦА  2 .     СИСТЕМНЫЕ ПЕРЕМЕННЫЕ ТР ДОС.

==================================================

АДР16 ! АДР10 ! ДЛИНА !       НАЗНАЧЕНИЕ

------!---------------!---------------------------

#5СВ6 ! 23734 ! 1*    ! Если = #F4 то INTERFACE 1

      !       !       !  не подключен.

      !       !       ! Если =00,то область пере-

      !       !       ! менных сдвигается для под-

      !       !       ! ключения ИНТЕРФЕЙСА 1

      !       !       ! и проверяется адрес 23832

------!-------!-------!---------------------------

#5СС2 ! 23746 ! 1*    ! Содержит код #С9 . При вы-

      !       !       ! зове подпрограмм ПЗУ SOS-

      !       !       ! из  ТР ДОС

------!-------!-------!---------------------------

#5СС8 ! 23752 ! 1     ! Содержит код #83 дла диско-

      !       !       ! вода  'А'

------!-------!-------!---------------------------

#5СС9 ! 23753 ! 1     ! Тоже самое для диска В

------!-------!-------!---------------------------

#5ССА ! 23754 ! 1     ! Тоже самое для диска С

------!-------!-------!---------------------------

#5ССВ ! 23755 ! 1     ! Тоже самое для диска  D

------!-------!-------!---------------------------

#5ССС ! 23756 ! 1*    ! Текущий сектор при чтении

      !       !       ! каталога

------!-------!-------!---------------------------

#5ССD ! 23757 ! 1*    ! При готовности дисковода

      !       !       !        =  #80

------!-------!-------!---------------------------

#5ССЕ ! 23758 ! 1*    ! При 0 - чтение ,при # FF

      !       !       !          запись  СЕКТОРА

------!-------!-------!---------------------------

#5СD6 ! 23766 ! 1*    ! = #FF   если комманда

      !       !       !           не выполнена

------!-------!-------!---------------------------

#5СD7 ! 23767 ! 2*    !  1.После проверки диска

      !       !       !      содержит

      !       !       !    количество цилиндров.

      !       !       !  2.Промежуточный адрес в

      !       !       !  озу, файлов типа <В и С>

--------------------------------------------------

#5СD9 ! 23769 ! 2*    !  1.Адрес обрабатываемого

      !       !       !    символа команды ДОС

      !       !       !  2.Промежуточная длина

      !       !       !  в ОЗУ файла типа <В и С>

------!-------!-------!---------------------------

#5СDB ! 23771 ! 2*    !  Промежуточная длина прог-

      !       !       !  раммы

==================================================

     Область заголовка (индификатора) файла

--------------------------------------------------

#5СDD ! 23773 ! 8     !  Имя файла , 8 знаков

------!-------!-------!---------------------------

#5СЕ5 ! 23781 ! 1     ! Тип файла  < В,С,D,# >

------!-------!-------!---------------------------

#5СЕ6 ! 23782 ! 2     ! При <С> типе - начало файла

      !       !       ! в озу.

      !       !       ! При <В> типе - длина прог-

      !       !       ! раммы.

------!-------!-------!---------------------------

#5СЕ8 ! 23783 ! 2     ! Рабочая длина  файла

------!-------!-------!---------------------------

#5СЕА ! 23786 ! 1     ! Объем файла в секторах

------!-------!-------!---------------------------

#5СЕВ ! 23787 ! 1     ! Номер первого сектора файла

      !       !       !       (0-15)

------!-------!-------!---------------------------

#5СЕС ! 23788 ! 1     ! Номер первой дорожки файла

==================================================

#5СЕF ! 23791 ! 1*    !  = 1 если подключен

      !       !       !      ИНТЕРФЕЙС 1

------!-------!-------!---------------------------

#5СF4 ! 23796 ! 1*    !  Номер сектора в текущий

                                   момент

------!-------!-------!---------------------------

#5СF5 !23797  ! 1*    !  Номер дорожки в текущий

      !       !       !            момент

------!-------!-------!---------------------------

#5СF6 ! 23798 ! 1     ! Номер активного дисковода,

      !       !       !            (0-3),

      !       !       ! для временной операции

--------------------------------------------------

#5СF7 ! 23799 ! 2     ! При выходе из 15616 содер-

      !       !       !          жит 0000

------!-------!-------!---------------------------

#5СF8 ! 23800 ! 1     !  Номер дисковода при

      !       !       !   операции с 2_мя файлами.

      !       !       !  Равен #FF, если канал

      !       !       !   открыт.

------!-------!-------!---------------------------

#5СF9 ! 23801 ! 1     ! 1.Номер дисковода при

      !       !       !     работе с 2мя файлами

      !       !       !           (0-3)

      !       !       !  2.Признак операции = #00

      !       !       !         LOAD FILE

      !       !       !

      !       !       !         VERIFY = #FF

      !       !       ! 3.Номер канала в который

      !       !       !   подается листинг каталога

      !       !       !    (экран=2 ,принтер=3)

------!-------!-------!---------------------------

#5CFA ! 23802 ! 1     !  Время перемещения головки

      !       !       !  дисковода  'А'   = #08

------!-------!-------!---------------------------

#5CFB ! 23803 ! 1     !  То же самое для диска В:

------!-------!-------!---------------------------

#5CFC ! 23804 ! 1     !  То же самое для диска С:

------!-------!-------!---------------------------

#5CFD ! 23805 ! 1     !  То же самое для диска  D:

------!-------!-------!---------------------------

#5CFE ! 23806 ! 1*    !  Текущая команда для

      !       !       !        1818ВГ93

------!-------!-------!---------------------------

#5CFF ! 23807 ! 1*    !   Номер сектора для

      !       !       !   подпрограммы

      !       !       !   чтение / запись секторов

------!-------!-------!---------------------------

#5D00 ! 23808 ! 2*    !  Текущий адрес буфера =

      !       !       !         ( #5D25 )

------!-------!-------!---------------------------

#5D02 ! 23810 ! 2*    !  Сохранение регистра  HL

------!-------!-------!---------------------------

#5D04 ! 23812 ! 2*    !  Сохранение регистра  DE

--------------------------------------------------

#5D06 ! 23814 ! 1     !  Количество знаков на поиск

      !       !       !             файла

      !       !       !  изначально = 09 ;

      !       !       !  имя файла + тип

------!-------!-------!---------------------------

#5D07 ! 23815 ! 1*    !  Счетчик удаленных файлов

------!-------!-------!---------------------------

#5D08 ! 23816 ! 1*    !  Первый символ в имени

------!-------!-------!---------------------------

#5D0C ! 23820 ! 2*    !  Состояние буфера ТРДОС

      !       !       !  для перекачки информации.

      !       !       !  (257 байт с адреса 23846 )

      !       !       ! 1. #FF -   Буфера нет

      !       !       ! 2. #00  -  Буфер включен

------!-------!-------!---------------------------

#5D0E ! 23822 ! 1*    !  Принадлежность комманды.

      !       !       !    #FF -  BASIC

      !       !       !   ИНАЧЕ   -  ТРДОС

--------------------------------------------------

#5D0F ! 23823 ! 1*    !  Код ошибки ТРДОС

      !       !       ! 1. #00 -  вводит пустую

      !       !       !           строку

      !       !       !     ИНАЧЕ   -   RETURN

------!-------!-------!---------------------------

#5D10 ! 23824 ! 1*    !  Старший байт кода ошибки

      !       !       !   при вызове п/п 15616

      !       !       ! Обнулять   принудительно

      !       !       !  при вызове  п/п 15616 !!

------!-------!-------!---------------------------

#5D11 ! 23825 ! 2*    !  Адрес командной строки

      !       !       !           ТРДОС.

      !       !       ! При вызове 15616 повторяет

      !       !       ! адрес командной строки SOS

      !       !       !        (23641)

      !       !       ! При вызове по 15619 равен

      !       !       !            23645

------!-------!-------!---------------------------

#5D13 ! 23827 ! 2*    !  Копия стека ошибок.

      !       !       ! При равенстве старшего

      !       !       !        байта #АА -

      !       !       ! происходит запуск 'boot'

      !       !       !         файла,

      !       !       ! а в 23833 записывается

      !       !       !   код   #FE

--------------------------------------------------

#5D15 ! 23829 ! 1*    !  При равенстве 0 печатает

      !       !       !   сообщения ТРДОС .

      !       !       !  Иначе не печатает.

------!-------!-------!---------------------------

#5D16 ! 23830 ! 1     !  Копия системного регистра

      !       !       !   ВГ-93 - порт #FF TRDOS

------!-------!-------!---------------------------

#5D17 ! 23831 ! 1     !  Вспомогательный байт.

      !       !       ! Если при вызове 15616

      !       !       !  байт НЕ РАВЕН #АА,

      !       !       ! то рисуется заставка ТРДОС

      !       !       ! и запускается файл 'воот'.

      !       !       !  Если байт равен   #FF  ,

      !       !       ! то система не попадает на

      !       !       ! подпрограмму'ОШИБКА' при

      !       !       ! чтении неверного АДРЕСНОГО

      !       !       ! МАРКЕРА.

------!-------!-------!---------------------------

#5D18 ! 23832 ! 1*    !  Если байт  равен   #FF ,

      !       !       !     то подключается

      !       !       !       ИНТЕРФЕЙС 1

------!-------!-------!---------------------------

#5D19 ! 23833 ! 1     !  Дисковод для работы

      !       !       !        (0-3)

------!-------!-------!---------------------------

#5D1A ! 23834 ! 2*    !  Внутренний адрес окончания

      !       !       !  подпрограмм ТРДОС  .

------!-------!-------!---------------------------

#5D1C ! 23836 ! 2*    !  Сохраняется SP

------!-------!-------!---------------------------

#5D1E ! 23838 ! 1     !  Системный номер файла в

      !       !       !   каталоге диска .

      !       !       !  ЕСЛИ ФАЙЛ ОБНАРУЖЕН!

------!-------!-------!---------------------------

#5D20 ! 23840 ! 3     !  Первые три символа введен-

      !       !       !    ной коммандной строки.

==================================================


ВНИМАНИЕ!  СИМВОЛОМ '*' В ТАБЛИЦЕ ОТМЕЧЕНЫ БАЙТЫ, КОТОРЫЕ НЕ РЕКОМЕНДУЕТСЯ 

МЕНЯТЬ В ПРОЦЕССЕ СТАНДАРТНОЙ РАБОТЫ С КОМАНДАМИ ТР ДОС !

==================================================









44444444444444444444444444444444444444444444444444444444444444444444444444444444444


                 ГЛАВА 4.

 МАКРО-ФУНКЦИИ  ТР ДОС (СТАНДАРТНЫЕ ПОДПРОГРАММЫ)


     Теперь, когда, в предыдущих главах, мы немного разобрались в структуре 

диска, рассмотрим как реализовать свои глубокие познания на практике и 

попытаться пообщаться с дисководом на языке АССЕМБЛЕР'а.

     Для облегчения жизни программистам, в ТРДОС выделены несколько функций, 

через которые можно управлять дисководом из АССЕМБЛЕРа.

     Назовем программирование,через эти функции - МАКРО УРОВНЕМ.

     Функции ТРДОС - это всего лишь несколько подпрограмм в ПЗУ, вызываемых 

через единственную точку входа, а адрес входа в эти подпрограммы для различных 

версий ТРДОС - ОДИН И ТОТ-ЖЕ!

     Все эти функции вызываются через  адрес:

     >>>>>>>>>>   #3D13   ( 15635 ). <<<<<<<<<<<<

     В регистр 'С' перед обращением к этому адресу, должен быть помещен номер 

вызываемой функции.


НАПРИМЕР:        LD    C,0      нулевая функция

                 CALL  #3D13    вызов функции


     При вызове большинства функций, (а их не много, не мало 21), система 

использует область системных переменных ТРДОС, а также использует перемещаемый

буфер.

     Так что проблемы снижения RAMTOP и затирания системной области ТРДОС 

остаются. Правда, есть функции, которые позволяют забыть об этом и выйти из 

любого трудного положения.

     Функции по назначению разделяются на четыре вида:

           1. Функции записи данных на диск.

           2. Фнкции считывания данных с диска.

           3. Функции управления контроллером.

           4. Вспомогательные функции.

Рассмотрим все функции подробнее.

--------------------------------------------------

          1. ФУНКЦИИ УПРАВЛЕНИЯ КОНТРОЛЛЕРОМ.

--------------------------------------------------

*  C=0    ВОСТАНОВЛЕНИЕ или  СБРОС микросхемы 1818ВГ93.

          Переход головок дисковода на дорожку 0.

           Опрашивается нажатие клавиши STOP

                      (C/SH+BREAK)

          Во время работы функции системная область ТРДОС НЕ ИСПОЛЬЗУЕТСЯ !

--------------------------------------------------

*  C=1    ВЫБОР ДИСКОВОДА.

          Номер выбираемого дисковода (от 0 до 4) помещают в регистр  'А'.

          Если по адресу 23802 (#5CFA) + НОМЕР ВЫБИРАЕМОГО ДИСКОВОДА записать 

          код #FF, то произойдет полная инициализация дисковода, определится 

          СИСТЕМНАЯ ОБЛАСТЬ ТРДОС, и туда запишется количество дорожек диска,

          величина позиционирования головок дисковода, и т.д.

          В ячейку 23798 (#5CF6) занесется номер выбранного дисковода.

          Во избежание нежелателных ситуаций рекомендуется: номер вызываемого 

          диска дублировать еще и в адреса 23800 (#5CF8) и 23801 (#5CF9).

          Функция активно использует системную область ТРДОС !

--------------------------------------------------

*  C=2    ПОЗИЦИОНИРОВАНИЕ (установка головок дисковода на нужный ЦИЛИНДР 

          диска).

          В регистр 'А' необходимо записать номер ДОРОЖКИ !!

          Дорожки с номерами 0,1 (верхняя и нижние стороны диска) будут 

          соответствовать ЦИЛИНДРУ номер 1, дорожки 2,3 - ЦИЛИНДРУ 2 и т.д.

          Быстро вычислить соответствие номера ДОРОЖКИ и номера ЦИЛИНДРА можно 

          по формуле:

                 N_ЦИЛИНДРА= INT (N_ДОР /2 )

--------------------------------------------------

*  C=#15  Проверка ЦИЛИНДРА.

          Регистр 'D' должен содержать номер проверяемого цилиндра.

          Сначала находится нужный цилиндр, затем проверяется сам цилиндр путем 

          считывания информации из системной области диска.

--------------------------------------------------

*  C=#16  ЗАГРУЗКА СИСТЕМНОГО РЕГИСТРА (РЕГИСТР УПРАВЛЕНИЯ КОНТРОЛЛЕРА, порт 

          #FF).

          Код управления контроллером заносится в регистр 'А'.

          Предварительно к нему прибавляется #3С (подробнее о портах 

          контроллера и командах управления -  смотри в последующих главах).

---------------------------------------------------

*  С=#17  Выбрать  нижнюю сторону диска (т.е., нечетные дорожки).

---------------------------------------------------

*  С=#18  НАСТРОЙКА НА ДИСК.

          Проверяется 8-й сектор нулевой дорожки. (Тип и емкость диска,

          количество цилиндров).


_______________________________________________________________________

         2. ФУНКЦИИ ЗАПИСИ ДАННЫХ НА ДИСК

--------------------------------------------------

*  С=9    ЗАПИСЬ В КАТАЛОГ ДИСКА ИНФОРМАЦИИ О ФАЙЛЕ.

           На диск записывается 16 байтов из области системного ОЗУ , т.е. 

           описатель файла из адреса 23773 (#5CDD)

           8 байтов -имя + 1 байт - тип (B,C,D,@) или др.,

           2 байта - начало в ОЗУ или длина Бейсика;

           2 байта - длина файла ;

           1 объем файла в секторах;

           1 байт  - номер первого сектора файла;

           1 байт -  номер первой дорожки файла;

           В РЕГИСТРЕ 'А' должен быть номер посадочного места в каталоге, куда 

           эта информация запишется,(т.е. номер файла).

---------------------------------------------------

*  С=#0С  ЗАПИСЬ БЕЙСИК ПРОГРАММЫ.


           С адреса 23773 (#5CD0) должны быть имя и тип файла.

           Если тип файла отличен от 'В', то файл записывается под именем 

           'ВООТ'.

--------------------------------------------------

*  С=#0В  ЗАПИСЬ ФАЙЛА.

           С адреса 23773 (#5CD0) должны быть;

           имя и тип фаила.

           В РЕГИСТРАХ 'HL' - адрес начала в памяти,

           В РЕГИСТРАХ 'DE' - длина файла.

--------------------------------------------------

*  С=6    ЗАПИСЬ ГРУППЫ СЕКТОРОВ

          Мощная подпрограмма записи информации из ОЗУ на диск, не требующая 

          для своей работы дополнительного буфера перекачки.

          Собственно область ОЗУ, которая должна сохраняться на диске, и будет 

          являться этим буфером.

          В регистр 'В' помещается количество СЕКТОРОВ, записываемых подряд на 

          диск (или проще - количество 256 байтных блоков из ОЗУ).

          В регистр 'HL' начальный адрес блока ОЗУ.

          В регистр 'D'  номер ДОРОЖКИ .

          В регистр 'Е' номер СЕКТОРА в ДОРОЖКЕ, с которого будет начинаться 

          запись.

          В регистре 'А' число 255 .


       !  ЕСЛИ регистр 'В' будет равен 0, запись производиться не будет.


----------------------------------------------------------------------------       

    3. ФУНКЦИИ  СЧИТЫВАНИЯ ИНФОРМАЦИИ С ДИСКА

----------------------------------------------------------------------------

*  С=#0А  ПОИСК ФАЙЛА В КАТАЛОГЕ

           Поиск файла производится по ИМЕНИ + ТИПУ файла.

           Шаблон имени должен быть помещен с адреса 23773 (#5CDD);

           8 символов имени + 1 символ - тип.

           Если по адресу 23814 (#5D06) изменить начальное значение = 9, на 

           меньшее, то поиск будет производиться по меньшему количеству 

           символов.

           Если шаблон имени найден в каталоге, то при выходе из подпрограммы 

           регистр 'С' будет содержать порядковый номер файла в каталоге;

           то же продублируется в адресах системной области  23828 (#5D14) и

           23823 (#5D0F).

           Если шаблон (файл) не найден,то старший байт регистра 'С' будет 

           установлен в 1, т.е. номер будет больше, чем 127 (#7F), а в адресе 

           23823 (#5D0F) установится 255 (#FF).

           Содержимое адреса 23828 (#5D14) не изменится.

--------------------------------------------------

*  С=8     ЧТЕНИЕ ИНФОРМАЦИИ О ФАЙЛЕ ПО ЕГО НОМЕРУ.

           В регистре 'А' должен быть номер интересующего Вас файла - (число от 

           0 до 127). После работы подпрограммы - 16 байт информации из 

           каталога диска перепишутся по адресу 23773 (#5CDD).

           Даже,если заданный номер в регистре 'А' будет указывать на стертый 

           файл или вообще на пустое место в каталоге, то и в этом случае из 

           диска что-то перепишется.

--------------------------------------------------

*  С=5     ЧТЕНИЕ ГРУППЫ СЕКТОРОВ С ДИСКА

            Мощная функция считывания информации с диска в ОЗУ, не требующая 

            для своей работы буфера перекачки, (буфером является та область,

            куда и считывается информация.

            В регистр 'В' помещается количество СЕКТОРОВ, считываемых подряд с 

            диска (или проще - количество 256 байтных блоков).

            В регистр 'HL' начальный адрес ОЗУ, куда будут считаны данные.

            В регистр 'D' номер ДОРОЖКИ.

            В регистр 'Е' номер СЕКТОРА в ДОРОЖКЕ, с которого будет начинаться 

            считывание.

            Регистр 'А' должен быть равным 0.

        !   Если регистр 'В' будет равен 0, то считывания в ОЗУ не произойдет.

--------------------------------------------------

*  С=#0Е  ЧТЕНИЕ / ПРОВЕРКА ФАЙЛА.

          Имя и тип файла должны быть сформированы с адреса 23773 (#5CDD).

          При 'А', равном 0 ,файл загружается по адресу, указанному в заголовке 

          файла в каталоге.

          При 'А', равном 3, файл загружается в адрес ОЗУ, указанном в регистре 

          'HL' и длиною из 'DE'.

          При 'А' равном 255 , файл загружается в адрес из регистра 'HL', 

          длиною равной длине файла, указанной в каталоге на диске.

          Если в системном адресе 23801 (#5CF9) будет записан 0, то происходит

          операция 'LOAD'; если #FF, то 'VERIFY'


--------------------------------------------------

           4. ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ.

--------------------------------------------------

*  С=3    Перенос содержимого регистра 'А' в ячейку ОЗУ 23807 (#5CFF)- номер 

          сектора для п/п чтения-записи сектора.

--------------------------------------------------

*  С=4    Перенос содержимого регистра 'HL' в ячейки ОЗУ 23808/23809 

          (#5D00/#5D01) - текущий адрес буфера перекачки.

---------------------------------------------------

*  С=7    ВЫВОД КАТАЛОГА ДИСКА в канал, указанный в регистре 'А'. При 'А'=2 - 

          каталог выводится на экран. (аналогично комманде ТРДОС 'САТ').

          При 'А'=3 - каталог выводится в принтер и т.д.

          При исполнении данной функции автоматически исполняется функция #18.

--------------------------------------------------

*  C=#12  Удаление ФАЙЛОВ.

           Перед вызовом функции, в адресе 23773 (#5CDD) должны быть 

           сформированы ИМЯ И ТИП ФАЙЛА, а в адресе 23814 (#5D06) - количество 

           знаков на поиск шаблона.

           Удаляются все файлы, попадающие под этот шаблон.

           Удаление происходит путем записи в первый символ имени файла кода 

           01, в области каталога диска.

-----------------------------------------------------------------------------

*  C=#13  Копирование 16 байт из области ОЗУ, указанной регистром 'HL' в ОЗУ по 

          адресу 23773 (#5CDD).

--------------------------------------------------

*  C=#14  Копирование 16 байт из области ОЗУ, с адресом 23773 (#5CDD), в ОЗУ,

          адрес которого указан регистром 'HL'.

--------------------------------------------------


                  ПРИМЕЧАНИЯ:

     При записи файла или заголовка файла на диск функциями, в отличии от 

команд ТРДОС, на диске можно образовать несколько файлов с идентичными именами, 

а, также, с типами файлов - отличными от стандартных.

     При работе с функциями не забывайте , что почти все функции изменяют 

область СИСТЕМНЫХ ПЕРЕМЕННЫХ ТРДОС, а, также, требуют объем свободной памяти,

приблизительно равный объему памяти при работе с КОМАНДАМИ ТРДОС.

     Самыми мощными, но и самыми 'опасными' функциями считывания/записи  

являются функции #05 и #06, которые, в основном, и применяются при загрузке

больших или нестандартных файлов (опасными потому, что при ошибке во время 

записи есть риск потерять диск или отдельные файлы на нем).

     Если внимательно рассмотреть порядковый номер заголовков файлов в каталоге,

то нетрудно заметить, что СТАРШИЙ полубайт номера соответствует номеру сектора 

на нулевой дорожке, а МЛАДШИЙ полубайт - номеру записи в секторе.

     Используя вышеперечисленные функции, можно писать довольно приличные и 

универсальные загрузчики и адаптировать большинство программ под дисковую 

систему.

     С помощью этих функций были адаптированы такие программы, как "ARTSTUDIO",

"TASWORD", "PASCAL", "GENS", и многие, многие другие, а уж об играх и говорить 

не приходится.


                    ГЛАВА 4.1

          ПРИМЕНЕНИЕ ФУНКЦИЙ ТРДОС.


     Рассмотрим несколько примеров и приемов программирования на основе функций 

ТРДОС.

                    Пример 1.

                    ---------

     В первую очередь, при адаптировании программы на диск, программа может  

определять: "А подключен ли вообще контроллер ?" (такая проблема может 

возникнуть, если Ваша программа универсальна, т.е. работает и на магнитофоне

и на диске).

     Используя функцию #13, это сделать довольно просто;


LD C,#13           функция  переноса байтов

LD HL,#0900        адрес окуда перенести 16 байт

CALL #3D13         перенос 16 байт в адрес #5CDD


                   (если контроллера нет,то CALL #3D13 равносильно RST #38

                   т.к. в пзу SOS по этому адресу расположен код #FF.

                   А RST #38 это программа опроса клавиатуры).


LD A,(#5CDD)       если контроллер есть, на время выполнения функции включится

                   ПЗУ ТРДОС и 16 байт перепишутся из адреса #0900, а там

                   код #FF


CP #FF             это ТРДОС ПЗУ ?

JR NZ, NODISK      нет - переход на работу с лентой


DISK     ....      да - инициализация диска


     Приблизительно, так же можно определить номер версии ТРДОС. Только в 

регистр 'HL' помещается число #0360 (по этому адресу, в ПЗУ ТРДОС находится 

текстовое сообщение:'ver 5.01 *'. В разных версиях ТРДОС текстовое сообщение 

может находиться по разным адресам, но, как правило, в пределах 255 байт от 

этого места).


start    LD C,#13           функция

         LD  HL,#0360       адрес  текста  версии

         CALL #3D13         перенос 16 байт в адрес #5CDD

         LD   A,(#5CDD)     загрузим первый байт сообщения


         CP   ...           здесь идет сравнение перенесенных байтов.

         ...........


     Как уж Вы будете сравнивать и с чем, - это уже зависит от Вашей фантазии.

Самое главное, что у Вас по адресу #5CDD есть 16 байт из ПЗУ ТРДОС. Не найдете 

номер версии по адресу #0360, прибавляйте (или отнимайте) от этого адреса по 16 

и выполняйте функцию снова.

     Можно поступить еще проще, если заранее знать у какой версии ТРДОС какие 

байты по одному и тому-же адресу различны.

     Составив таблицу байтов из этого адреса, но из разных версий ТРДОС, Вы 

будете проверять всего лишь по одному байту, сравнивая его с таблицей.

     Такое определение версии ТРДОС необходимо в том случае, если в Вашей 

программе используются методы программирования непосредственно микросхемы ВГ93

через процедуры ПЗУ. А так как у разных версий ТРДОС эти процедуры расположены  

по разным адресам, то определение версии ТРДОС является необходимым для 

нормальной работы Вашей программы в любом компьютере, а не только в Вашем!

     С главы 5 этой книги начнется материал, как раз и посвященный таким 

методам программирования, а пока...


                    Пример 2.

                    ---------

Попробуем теперь распечатать каталог диска 

        .. на экране

         LD A,02     номер канала для вывода

                     (экран)

         LD C,07     функция

         CALL #3D13  вывести

;

        .. на принтере

         LD A,03     номер канала ( принтер)

         LD C,07     функция

         CALL #3D13  вывести на принтер

;


     Конечно, когда Вы будете выводить каталог на принтер, необходимо сначала 

загрузить драйвер принтера с диска или из ПЗУ (если, конечно, он у Вас 'зашит'

в ПЗУ) и включить принтер.

     В связи с этой программкой возникает интересная возможность: если 

вспомнить, что последовательные файлы ТРДОС резервируют каналы с 4 по 15, то 

что нам мешает 'распечатать' каталог в ФАЙЛ?  Надо только открыть файл, связать 

его с номером канала, и в этот канал вывести каталог.

     На БЕЙСИКЕ это бы выглядело так:


10 RANDOMIZE USR 15619:REM:OPEN# 4,"CATALOG",W

20 RANDOMIZE USR 15619:REM:CAT #4

30 RANDOMIZE USR 15619:REM:CLOSE# 4


     Вместо оператора 'САТ' можно использовать и 'LIST'. Тогда в ФАЙЛ "CATALOG" 

запишется полная информация о файлах на диске.

     Таким образом, можно в файлах хранить каталоги всех дисков которые у Вас 

есть. А если в каком-нибудь 'ДИСК  ДОКТОРЕ' исправить тип файла в каталоге с

'#' на 'С' , то этот файл можно загрузить в текстовый редактор , например TLW .


     Рассмотрим теперь несколько примеров считывания/записи на диск. Опустим 

только случаи , когда условия  благоприятны;  т.е. RAMTOP установлена достаточно 

высоко, область системных переменных БЕЙСИКА и ТРДОС не затронута (как в таких 

условиях произвести считывание/запись, я надеюсь, Вы разберетесь сами).

     Разберем более тяжелые случаи адаптации программы с магнифона на диск ...


                    Пример 3.

                    ---------

     RAMTOP программы расположена очень низко, программа с магнитофона 

загружается загрузчиком в машинных кодах  с адреса, например, 23867 (#5D3B), 

т.е. прямо в область БЕЙСИКА. Но области системных переменных БЕЙСИКА и ТРДОС 

не затронуты.

     Первое, что необходимо сделать - это просто перекопировать программу для  

адаптации с ленты на диск, каким-нибудь копировщиком (TAPE -> DISK).

     При работе со скопированным файлом надо знать, что длина файла и адрес 

загрузки в заголовке файла, как правило, указаны НЕВЕРНО! Такое встречается в 

90% магнитофонных программ.


             СЧИТЫВАНИЕ ФАЙЛА С ДИСКА


     Для загрузки файла с диска, нам нужен настоящий адрес посадки программы в  

ОЗУ и реальная длина блока программы. И, конечно, необходим адрес запуска 

программы.

     Очень надеюсь, что взламывать магнитофонные загрузчики Вы уже умеете и, 

в конечном итоге, у Вас есть все то ,что нужно для работы.

     В любом случае, нам  необходимо  использовать ТРДОС функцию  С=5, т.е. 

непосредственно считать сектора  файла, или прямо по нужному месту в ОЗУ,

или через какой нибудь буфер (например, экранный).


;-------- LOADER -----

; СТАРТ прогграммы с метки START

;

; для начала поместим имя и тип интересующего нас

; файла (9 байт) по адресу #5CDD.

; затем вызовем функцию ПОИСК файла (#0A).

; и если файл обнаружен , то прочтем информацию о

; нем т.е. весь заголовок файла из каталога.


FILENAME    DEFM  "filename" ; имя файла

            DEFM  "C"        ; тип файла


START       LD HL,FILENAME   ; перенесем 9 байтов

            LD DE,#5CDD      ; в системную

            LD BC,9          ; область ТРДОС

            LDIR


FIND        LD C,#0A      ; поиск заданного

            CALL #3D13    ; файла

            BIT 7,C       ; такой файл есть?

            JP NZ,NO_FIND ; нет, выход

            LD A,C        ; есть, тогда

            LD C,8        ; прочтем о нем

            CALL #3D13    ; информацию

; Теперь с адреса #5CDD у нас есть вся информация

; о файле для его загрузки.

; Нам нужно взять номер сектора, номер дорожки и

; занести  в  регистр 'DE', в регистр 'В'-

; количество  секторов, а в регистр 'HL'- адрес

; загрузки в ОЗУ.


LOAD        LD DE,(#5CEB)      ; в 'D' номер трека

                               ; в 'Е' сектор

            LD BC,(#5CE9)      ; в 'В' количество

            LD HL, ADRES       ; адрес загрузки

            LD C,05            ; функция

            CALL #3D13         ; загрузить


     Если реальная длина загружaемого блока в байтах кратна 256 или у Вас есть 

уверенность, что последние 'лишние' байты последнего загружаемого сектора

не затрут нужную информацию в ОЗУ, то чтение секторов можно производить прямо 

по адресу загрузки. Если нет - то загрузку необходимо производить через буфер  

или загружать в буфер последний сектор файла, и оттуда переносить нужные 

последние байты на место (в этом случае, Вам необходимо вычислить номер 

последнего загружаемого сектора.)

     Можно, конечно, написать загрузчик таким образом, чтобы он загружал всю 

программу через буфер в 256 байт. К примеру: нам надо загрузить файл длиною в 

40100 байтов, в адрес 24000. 40100  байт равны 157 секторам на диске (156 

секторов + 164 байта из 157 сектора).


;------------- LOADER 2  -------

; Загрузчик программы через буфер в 256 байт.

; регистр 'В' занести  количество секторов

; регистр 'D' - номер первого сектора файла на

;                диске

; регистр 'Е' - номер начальной дорожки файла

;

         LD SP,STEK     ; установим стек на

                        ; свободное место в

                        ; ОЗУ,в нашем случае

                        ; можно на 65535

START1   LD HL,BUFFER   ; адрес буфера перекачки,

                        ; (к примеру 16384 )

         LD C,05        ; функция чтения

         PUSH BC        ; спрячем

         LD B,1         ; количество загружаеных

                        ; секторов за один раз

         CALL #3D13     ; загрузим с диска в буфер

                        ; 1 сектор = 256 байт

         LD HL,BUFFER   ; начало буфера

         LD DE,(ADRES)  ; истинный адрес посадки

                        ; в ОЗУ

         LD BC,256      ; 1 сектор

         LDIR           ; перенесем загруженный

                        ; сектор туда,

                        ; где он должен быть

         LD (ADRES),DE  ; спрячем адрес, куда пере-

                        ; носить следующую порцию

         LD DE,(#5CF4)  ; возьмем из системной пе-

                        ; менной ТРДОС - ТЕКУЩИЙ

                        ; номер сектора и ТЕКУЩИЙ

                        ; номер дорожки ФАЙЛА

                        ; (при исполнении функции

                        ; эти данные заносятся

                        ; в адрес #5CF4  системой

                        ; автоматически )

         POP BC         ; востановим общее ко-

                        ; личество загружаемых

                        ; секторов

         DJNZ START1    ; все сектора считаны ?

                        ; нет - загружай еще

         LD HL,BUFFER   ; да, тогда подгрузим

                        ; последний сектор

         LD C,05        ;

         LD B,01

         CALL #3D13

         LD HL,BUFFER   ; и занесем его на

         LD DE,(ADRES)  ; место

         LD BC,164

         LDIR

         JP...          ; стартовать программу


ADRES    DEFW 24000     ; здесь адрес загружаемой

                        ; программы

;----------------

     Конечно, данный пример не очень изящен, и опытный читатель сразу заметит 

- вот тут бы подпрограммочку, а тут можно было бы по другому. Все правильно, 

данный пример показывает принцип загрузки, а уж как Вы этот принцип примените,

это дело Ваше.

     При необходимости, можно отказаться от опроса системной переменной  #5CF4 

и написать два вложенных цикла; один из которых считал бы сектора и при 

переходе через число 16 (16 секторов на дорожке), увеличивал бы номер дорожки 

на единицу (не забудьте, что функции ТРДОС работают с номерами секторов от 0 

до 15). Загрузчик увеличился бы в объеме, но выиграл бы в универсальности.

     И еще: данный способ загрузки через буфер страдает некоторой 

медлительностью (в 1,5 раза медленней, чем это делает ТРДОС). Но этот метод 

работает, и работает неплохо.

     Если областью работы данного загрузчика выбрать, к примеру, экран, на 

экран же определить стек и буфер перекачки, то, в принципе, можно загружать 

ЛЮБЫЕ файлы, не попадающие на системную область ТРДОС. Или, вообще, любые файлы, 

если загружать блоки программы, затирающие системную область, сначала в 

промежуточный буфер и, перебрасывая в конце загрузки эти блоки на нужное место.

Правда, это будет удобнее сделать другим методом, но об этом мы расскажем в 

главах, посвященных программированию непосредственно микросхемы 1818 ВГ93 .


* НЕСКОЛЬКО ОБЩИХ ЗАМЕЧАНИЙ И ДОПОЛНЕНИЙ  НА БУДУЩЕЕ *


- дисковая система не любит ВТОРОГО ПРЕРЫВАНИЯ, так что первой Вашей командой 

должна быть команда IM1 и DI;

- в любом случае, лучше всего запретить прывания вообще или запрещать их перед 

обращением к диску;

- если у Вас 2 и более дисковода, то выберите сначала номер дисковода, с 

которым будете работать;

- при первом опробовании Ваших программ желательно ЗАЩИТИТЬ ДИСК ОТ ЗАПИСИ -

заклейте прорезь на диске, и Вы избежите случайных неприятностей


                    ПРИМЕР 4.

                    ----------

     Теперь давайте рассмотрим случай более 'тяжелый'. Системные переменные 

ТРДОС и системная область БЕЙСИКА затерты кодами программы. Стек в программе 

установлен или на область экрана, или на самую верхушку ОЗУ, или в другое место.

Программа в своей работе догружает другие уровни или свое состояние.

     Для начала необходимо выбрать место для нашей подпрограммы загрузчика.

Это может быть та область, где помещались коды загрузчика с ленты, а если места 

там не хватит, то это может быть место, где располагается подпрограмма вывода 

имен создателей программы в заставке (иногда такие подпрограммы в заставках 

занимают до нескольких килобайт памяти).

     А если, все-таки, места свободного в программе нет, то можно впихнуть свою 

"загрузку" в какую-нибудь второстепенную подпрограмму или пожертвовать частью 

какой-нибудь картинки. В общем, если захотеть, можно выкрутиться. В крайнем

случае можно пожертвовать и звуком в программе.

     Самая тяжелая ситуация, с которой приходится сталкиваться - это 

загрузка-выгрузка состояния игры в программах  типа "SIM SITY", "ELITE",

"TAY 2" и т.д.

      В этих программах, казалось, байта лишнего не выкинешь, но,"разложив" 

программу по полочкам, может оказаться, что из нее безболезненно можно было бы 

удалить 0.5 - 1 килобайт. А для дискового загрузчика этого ПРЕДОСТАТОЧНО !

     Но вот свободное место в программе найдено, подсчитана длина и место 

посадки загружаемого блока в программе. Теперь осталось только загрузить...

     Для начала инициализируем область системных переменных ТРДОС ;


START   LD HL ,#5C00      ; сохраним область ОЗУ

        LD DE ,BUFFER     ; с #5C00 по #5DF4

        LD BC ,#01F4      ; в другое место

        LDIR              ; (например экран)


        LD HL ,#5C00      ; очистим это место

        LD DE ,#5C01      ; на всякий случай

        LD BC ,#01F3

        LD (HL),L

        LDIR

        LD (ADRES),IY     ; спрячем текущее значение

                          ; регистра IY

        LD IY,#5C3A       ; инициализация индексного

                          ; регистра на область пере-

                          ; менных SOS

        IM 1              ; прерывание 1

        LD A ,#FF         ; переменная SOS код сооб-

        LD (#5C3A),A      ; щения об ошибке

        LD (#5D0C),A      ; переменная ТРДОС -буфер

                          ; перекачки отсутствует

        LD A ,#C9         ; переменная ТРДОС для

        LD (#5CC2),A      ; возврата из подпрограмм

        LD A,#83          ; код для дисковода 'А'

        LD (#5CC8),A


LOADFILE CALL LOADER       ; загрузим файл с диска

         LD IY,(ADRES)     ; востановим все

         LD HL,BUFFER      ; на

         LD DE,#5C00       ; старое

         LD BC,#01F4       ; место

         LDIR

RETURN   JP...(RET)        ; выйдем в программу


;- -  а это сам загрузчик    - - - - - - - - - - -


LOADER   DI

         LD C,01           ; выберем дисковод 'А'

         LD A,0

         CALL #3D13

         LD HL,FILENAME    ; перенесем ИМЯ и ТИП

         LD DE,#5CDD       ; файла в системную

         LD BC,9           ; область

         LDIR

         ...               ; И ТАК ДАЛЕЕ, СМОТРИ

                           ; ПРЕДЫДУЩУЮ ПРОГРАММУ ...

;- - - - - - - - - - - - - - - - - - - - - - - - - - -


     Вот таким, примерно,  способом и загружаются не только уровни игры в 

программах, но иногда и сама программа тоже.


 !  Если в процессе своей работы, программа использует прерывание 2, то на 

выходе из загрузчика не забудьте его вернуть программе!


                    ПРИМЕР 5.

                    ---------

     А вот другой, довольно оригинальный, метод загрузки уровней для программ,

с использованием функции #0Е: чтение  ФАЙЛА через буфер ТРДОС. Правда, 

этот метод требует много места в ОЗУ.

     В теле основной программы Вы должны найти место не только для своего 

загрузчика, но и для копии всей инициализированной области системных переменных

ТРДОС, и в нужный момент перенести всю область на свое исконное место.


;      loader level game

;

START   LD DE,#5CC8       ; перенесем копию систем-

                          ; ной

        LD HL,BUFFER_1    ; области на место

        LD BC,#32

        LDIR

        LD A,(LEVEL)      ; здесь содержится номер

                          ; загружаемого уровня

        ADD A,#31         ; приведем номер к коду

                          ; цифр таблицы ASC

        LD (#5CE4),A      ; занесем номер уровня в

                          ; имя файла

        DI

        LD HL,BUFFER_2    ; перенесем вторую поло-

                          ; вину

        LD DE,#5CFB       ; копии системной области

        LD BC,#20         ; ТРДОС на место

        LDIR

        LD (ADRES),IY     ; спрячем текущее значе-

                          ; ние IY

        LD IY,#5C3A       ; занесем адрес системной

                          ; области БЕЙСИКА (SOS)

        LD (IY+00),#FF    ; инициализируем адрес

                          ; кода ошибки

        IM 1              ; прерывание 1

        LD A,#C9          ; код возврата из под-

                          ; програм

        LD (5CC2),A       ; занесем в область SYS 

                          ; TRDOS

        LD HL,#5D27       ; адрес вершины стека 

                          ; калькулятора

        LD (#5C65),HL     ; БЕЙСИКА


        XOR A             ; адрес загрузки взять из

                          ; каталога на диске

        LD C,#0E          ; загрузить файл адресом 

                          ; и

        CALL #3D13        ; длиною, как в каталоге

        ...               ; здесь может идти проверка

                          ; загрузился ли файл и тот ли

                          ; файл загрузился.

        RET               ; выход в программу

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

     В этом случае, во всех заголовках загружаемых файлов в каталоге должна 

быть указана реальная длина и истинный адрес загрузки блоков.


                    ПРИМЕР 6.

                    ---------

     Рассмотрим еше один способ загрузки с диска: это, так называемый, метод 

"склеенных" файлов. На диске такие файлы выглядят единым файлом БЕЙСИКА, но 

содержат в себе и коды экрана-заставки и рабочие коды программы. Такие файлы,

на мой взгляд, не являются крайней необходимостью, но они есть, и с ними надо 

считаться.

     Способ создания таких файлов прост... (естественно, что простыми командами 

ТРДОС тут не обойтись, и загружать такой файл мы будем своим загрузчиком).

     При создании такого файла желательно, чтобы файлы на диске располагались  

по порядку их загрузки, ПОСЛЕ файла БЕЙСИКА (можно, конечно их расположить в 

любом порядке, но это очень усложнит Вам вычисления в процессе работы Вашего  

загрузчика). Ну, и, естественно, эта программа не должна содержать других,  

старых  -  "чужих" подпрограмм загрузки с диска , а то может произойти конфликт.

     Захотелось, к примеру, Вам объединить в 'склееный' файл программу 

"PULSE WARRIOR ". Вся программа состоит из 3х файлов:


"PULSE.WR" - файл БЕЙСИКА

"pulse.w$" - файл экрана

"pulse.w1" - файл основного тела программы

     Из программы БЕЙСИКА мы узнаем ,что сначала грузится файл экрана, а затем 

основные коды программы по адресу 24500. RAMTOP снижена оператором CLEAR на  

24499. Стартует программа по адресу 24500 , а затем по адресу 62003.

     Для начала на диск запишем простую программу на БЕЙСИКЕ под именем 

"PULSE.W" и зарезервируем в первой строке, после оператора REM, область под наш

загрузчик .

1  REM : В этой строке резервируется место под наш

         загрузчик; (эдак, байтов на 40)

2  RANDOMIZE USR 23872 :REM: А это запуск загрузчика


     Далее, на ДРУГОЙ диск копировщиком копируем 2 файла

          " pulse.w$" и "pulse.w1"

И теперь нам потребуется программа, которая могла бы редактировать сектора 

диска на нулевой дорожке, т.е. область каталога.

     Это  может быть "DISK DOCTOR", а может, Вы захотите написать такую 

программку сами: тех знаний, которые Вы уже получили, дочитав до этого места -

вполне  хватит (всего лишь и надо считать с диска в ОЗУ первые СЕМЬ секторов 

нулевой дорожки, изменить с помощью какого-нибудь MONS'а в этой области ОЗУ 

всего лишь 1 байт и записать эту область ОЗУ на диск туда же, откуда и 

считывали).

     В области каталога находим имена наших программ и внимательно смотрим на 

байты, отвечающие за ОБЪЕМ ФАЙЛА В СЕКТОРАХ (14-й байт от первой буквы имени 

файла). В нашем случае это будет (к примеру):

   у файла  " pulse.w$" -  27 секторов ( #1B ),

   а у       "pulse.w1" - 101 сектор   ( #65 ).

     Складываем в уме 101 и 27 и получаем 128 (# 80) секторов. Аккуратно 

вписываем эту цифру в заголовок файла "pulse.w$" вместо цифры 27 (#1В) и 

записываем изменения на диск.

     Теперь у нас файл "pulse.w$" по объему секторов стал равен объему обоих 

файлов, т.е. он стал содержать в себе и экран, и рабочие коды программы.

     Возвратимся к первому диску, и в файле БЕЙСИКА в зарезервированном  месте  

первой  строки, с адреса 23872 (#5D40), любым способом вписываем коды 

следующей программы:


; КОДЫ в 'HEX'   МНЕМОНИКА

                 ORG #5D40      ; адрес первой

                                ; строки 23872

 31,B3,5C        LD SP,#5FB3    ; CLEAR 24499

 F3              DI

 ED,5B,F4,5C     LD DE,(#5CF4)  ; текущая дорож-

                                ; ка и сектор

 21,00,40        LD HL,#4000    ; адрес экранной

                                ; области

 01,05,1B        LD BC,#1B05    ; 27 секторов или

                                ; 6912 байт

                                ; и функция

                                ; #05 в 'С'

 CD,13,3D        CALL #3D13     ; загрузить на

                                ; экран.

 ED,5B,F4,5C     LD DE,(#5CF4)  ; взять следующие,

                                ; текущие

                                ; дорожку и сектор

 21,B4,5F        LD HL,#5FB4    ; адрес 24500

 01,05,65        LD BC,#6505    ; 101 сектор

 CD,13,3D        CALL #3D13     ; загрузить сектора

                                ; в ОЗУ

 CD,B4,5F        CALL #5FB4     ; первый старт прог-

                                ; раммы

                                ; по адресу 24500

 C3,33,F2        JP F233        ; второй старт прог-

                                ; раммы

                                ; по адресу 62003

     Теперь, как только коды загрузчика окажутся на месте, и БЕЙСИК-программа 

запишется на диск, например, под именем " P.W.", нам остается со второго

диска скопировать сюда наш склеенный файл.

     И опять программой ДИСК ДОКТОР внесем изменения в область каталога.

А именно, посмотрев сколько занимает секторов наш БЕЙСИК-файл с загрузчиком,

складываем (опять в уме) это число с объемом нашего 'склеенного' файла и 

вписываем полученный результат в байт 'количество секторов' в БЕЙСИК заголовок.

     В нашем случае количество секторов у БЕЙСИК файла равно одному, а общее 

количество стало быть 129 (#81).

     Все -  у нас на диске оказался 'склеенный' файл с именем "P.W." и типом 

БЕЙСИК. Остальные файлы можно безболезненно стирать.

     При запуске такого файла система загрузит поначалу только БЕЙСИК часть, а 

уж БЕЙСИК нашим загрузчиком догрузит все остальное.

     И последнее, что можно сказать по теме 'Считывание с диска', это то, как 

можно разделять большие файлы ,записанные на диск с магнитофонной ленты.

В кассетных версиях, есть такие программы, которые загружаются единым файлом,

начиная с экрана и почти до конца ОЗУ компьютера. Такие файлы могут  иметь  

длину до 45 Килобайт и больше, есть файлы даже в 49 Килобайт. Таких программ 

много, и возникает вопрос:

                " ЧТО ДЕЛАТЬ ?"

     Один способ известен уже давно - это применить популярный кассетный 

копировщик  "COPY-COPY" для разделения программы на несколько кусков. Такими 

составными коммандами копировщика, как

     * LOAD (ХХ    - загрузка первых ХХ  байтов файлa;

     * LOAD (ХХ TO - загрузка файла БЕЗ первых ХХ байтов,

можно изловчившись за 1-2 раза разделить любой файл. Для дисковых версий такие 

большие файлы легче делить на куски длинной кратной 256 (т.е. размеру сектора).

Выделить, к примеру, кусок экрана:

           длина = 6912 байт = 27 секторам .

Второй кусочек областью от адреса 23296 до 25343

           длина = 2048 байт= 8 секторам.

И последнй кусок - с адреса 25344 и до конца файла.

     Переписав без особого труда эти кусочки на диск, мы сначала будем загружать 

экран, затем кусок с адреса 25344 и оставшийся кусочек в 2 Килобайта в экранную 

область. Единственное, что нам останется сделать, так это предварительно вписать 

в свободное место на экране, или в другое место, коротенькую программу 

переброса этих самых 2048 байт на свое законное место, т.е. на адрес 23296. 

Последней строкой в БЕЙСИКЕ у нас должна находиться строка RANDOMIZE USR <адрес 

программы переброса>, а последними строками программы переброса - команды

            LD SP,STACK  ; 'родной' стек программы

            JP START     ; запуск программы


     Но есть и другой способ разделения файлов. Если у вас файл 'влез' в 

копировщик "TAPE <-> DISK" (например, копировщик "L-COPY" может записать файл 

длиною в 45056 байтов), то такой файл можно разделить и на диске (если у Вас 

компьютер 128 Килобайт ,то копировщик ТD.COPY может взять в себя любой 

сверхдлинный файл с ленты и записать его на диск).

     Но вот Вы скопировали файл с ленты на диск... После копирования, на диске 

у Вас получился файл длиною в 45056 байт (#В000), или 176 (#В0) секторов и 

адресом загрузки 16384 (#4000). Стартуем программу 'DISK DOKTOR' и выходим в 

режим редактирования НУЛЕВОЙ дорожки. Находим заголовок нашего файла,

(предположим имя файла у нас - "FILE 1"). Внизу этого заголовка создаем еще 2 

заголовка (каждый заголовок занимает 16 байт).

 -  сначала впишем имена "FILE 2" и "FILE 3";

 -  затем  в  обоих  файлах пометим ,что это коды, вписав после имен файлов 

    символ 'С';

 -  после идут 2 байта длины программы в байтах. Сделаем файл "FILE 2" длиною в 

    2048 (#0400) и адресом загрузки 23296 (#5В00), а "FILE 3" - длиною

в 36096 (#8D00) и адресом загрузки 25344 (#6300). Файл же "FILE 1" будет 

экраном, загрузка в 16384 (#4000) и длиною 6912 (#1В00);

 -  изменим во всех заголовках байты, отвечающие за адрес загрузки, (два байта) 

и длины (два байта);

 -  затем изменим байт, отвечающий за количество секторов;  первый файл у нас 

экран, длиною в 27 (#1В) секторов, второй #04 сектора и последний 141 (#8D) 

секторов.

 - теперь начнем изменять последние два байта в каждом загловке: байт 'номер 

первого сектора файла' и байт 'номер начальной дорожки файла';

 - изменять заголовок файла "FILE 1" мы не будем, а только возьмем из него 

значение байта "СЕКТОР" и байта " ДОРОЖКА";

 - подсчитаем, какой номер Начального сектора у нас будет во втором файле 

"FILE 2". Сложим номер начального сектора файла "FILE 1" с объемом файла в 

секторах. Полученный результат НАЦЕЛО разделим на 16. ОСТАТОК от деления есть 

НОМЕР начального сектора файла "FILE 2" (если остатка нет, то номер сектора 

будет 0);

 - число же, получившееся при делении, надо сложить с числом 'НОМЕР ДОРОЖКИ' 

файла "FILE 1", и мы получим номер дорожки файла "FILE 2" (естественно, если 

результат деления меньше единицы, то это НОЛЬ, и складывать номер дорожки надо 

с НОЛЕМ, т.е. следуюший файл расположен на этой же дорожке);

 - вписываем полученные резултаты вычислений СЕКТОРА и ДОРОЖКИ в заголовок 

файла "FILE 2".

 - точно такие же вычисления произведем для последнего файла, "FILE 3",

только исходя уже из данных файла "FILE 2".

 -  после того, как все подсчитано, все байты записаны, нам остается изменить 

один байт на ВОСЬМОМ системном секторе нулевой дорожки, а именно - байт  

"количество файлов на диске". Расположен он на 228 (#Е4) месте от начала 

сектора. Увеличим его значение на 2;

 -  если Вы сделаете все правильно, то у Вас на диске теперь будет вместо 

одного большого - 3 файла, и Вам останется только написать для них программу

загрузки (что не составит для Вас, я надеюсь, теперь большого труда).

 Приблизительно так же можно разделить дисковый MAGIC- файл, и создав свой 

загрузчик запускать его не командой GOTO , а 'нормальными' командами. Но об 

этом чуть позже - MAGIC-файлам будет посвящена отдельная глава.


     А теперь поговорим о ЗАПИСИ ...

     Наиболее часто ЗАПИСЬ файла на диск необходима в системных программах, где  

активно идет обмен информацией с различными файлами. Это и БАЗЫ ДАННЫХ, и 

ЭЛЕКТРОННЫЕ ТАБЛИЦЫ, и ПОИСКОВЫЕ СИСТЕМЫ. Или это файлы, где необходимо 

сохранять и считывать пользовательские наработки (РЕДАКТОРЫ ТЕКСТОВ, ГРАФИЧЕСКИЕ

РЕДАКТОРЫ, АССЕМБЛЕРы, и др.).

     В некоторых случаях проблема решается средствами стандартных команд ТРДОС,

в некоторых средствами ФУНКЦИЙ ТРДОС, а в иных (таких мало), прямым

программированием контроллера (например, текстовый редактор WORD, различные 

D.C.U., A.D.M., форматеры, исправители дисков, система IS.DOS и др.). В играх 

ЗАПИСЬ используется в случаях, когда нужно сохранить состояние игры ("SIM SITY",

"ELITE", "TAU CETI"), или когда необходимо записать результаты игры - таблицу 

выигрышей.

     Как и в предыдущих случаях, мы не будем рассматривать методы, основанные 

на командах ТРДОС и случаи, когда всего везде хватает. Единственное, что можно  

сказать по этому поводу, так это то, что такими способами, (когда все 

"тип-топ"), адаптированы графический редактор "ARTIST  2", текстовый редактор 

"T.L.W.+" и многие другие программы.

     Для случаев, когда можно использовать функцию #0В - запись файла и #0С  -  

запись файла БЕЙСИКА, единственное  добавление: Ваша программа сначала должна 

проверить имя Вашего записываемого файла на предмет отсутствия такого же имени 

на диске. И если такое имя на диске есть, то 'задать' вопрос о смене введенного  

имени или о стирании старого файла с диска.

Если этого не сделать, то у Вас на диске могут оказаться файлы с идиентичными 

именами и типами, а это недопустимо, если поступать по правилам!

     Рассмотрим, в идеале, что должна делать программа, которая записывает 

данные на диск в виде файла:

1. Выбрать дисковод ( #01 функция).

2. Настроить диск ( #18 функция).

3. Запросить у пользователя имя файла (длину, адрес начала и тип ), а если надо,

   то распечатать на экране каталог диска.

4. Перенести введенную информацию в системную область, в адрес #5CDD.

5. Дать команду "ПОИСК файла на диске".

6. Если имя такое есть, запросить об удалении старого или об изменении 

   введенного имени.

7. Удалить старый файл, если нужно.

8. Узнать из 8-го системного сектора диска первый СВОБОДНЫЙ сектор и дорожку, 

   а, также, количество свободных секторов на диске.

9. Подсчитать, хватит ли места на диске для Вашей программы, и если нет, то 

   попросить сменить диск.

10.Узнать из 1 - 7 сектора каталога первое свободное место для заголовка (по 

   символу 0).

11.Если все в норме, то записать полный заголовок Вашего файла в область 

   каталога на первое свободное место.

12.Записать блок данных из ОЗУ на диск с первого СВОБОДНОГО сектора диска.

13.Скорректировать данные на 8-ом системном секторе диска, а именно:

         байт 'количество свободных секторов',

         байт 'количество удаленных файлов',

         байт ' первый свободный сектор',

         байт ' первая свободная дорожка',

         байт ' число файлов на диске',

14. Выйти в основную программу.

     Конечно, на практике половина этих пунктов не соблюдается; для этого бы 

потребовалось слишком много места в ОЗУ, но некоторые пункты являются 

обязательными.

     Встает вопрос: "Как же впихнуть такую огромную подпрограмму в, и без того,     

ограниченное пространство ОЗУ игровой или системной программы" ?

     Если внимательно присмотреться, то можно заметить, что, как для загрузки,

так и для записи, МИНИМАЛЬНЫЕ данные которые нам требуются - это:

      . номер первой дорожки файла,

      . номер начального сектора файла,

      . реальная длина сохраняемых/загружаемых блоков.

И если точно знать первые два параметра и передавать их в подпрограмму ЗАПИСИ,

то можно избавиться от многих проблем. Но как же узнать эти самые параметры и 

как вносить эти все изменения в системную область диска? Да при помощи БЕЙСИКА!

Он нам будет и создавать файл и делать все остальное...

     Он же передаст все параметры этого файла в программу.

Назовем этот способ работы:

          'МЕТОДОМ ФИКСИРОВАННОГО ФАЙЛА'

                      ----

В  поцессе ВЗЛОМА адаптируемой программы, скинем на диск область ОЗУ, где лежат  

те данные, которые программа записывает и считывает во время своей работы.

Это  нужно делать в первичном состоянии игры, т.е. тогда, когда в области 

данных не произошли изменения.

     Предположим, что область данных программы занимает у нас 256 байт. После  

записи на диск у нас получится файл состояния игры, например, с именем 

"level 0" и длиною 256 байт. Теперь нам надо написать БЕЙСИК файл, который 

будет изначально формировать файл состояния игры, а также запускать игру и  

передавать данные о файле-подпрограмме работы с диском.

     Сделаем запрос имени файла, с которым потом будет работать программа, и 

перезапишем данные из файла "level0 " в файл с введенным именем.

10 INPUT " FILE ACCES ? "; A$

20 RANDOMIZE USR 15619:REM:LOAD"level 0"CODE 40000

30 RANDOMIZE USR 15619:REM:SAVE A$ CODE 40000,256

Теперь, когда файл состояния пересоздался (продублировался) с введенным именем,

нам нужно найти всего лишь НОМЕР его начального СЕКТОРА и НОМЕР ДОРОЖКИ.

Это без труда делается функцией:

           #0А - поиск файла по имени и типу (имя и тип файла по адресу 

системных переменных у нас уже есть - результат работы строки 30 БЕЙСИКА),

а затем функцией:

           #08 - чтение информации о файле.

     После этого осталось лишь сохранить в какой-нибудь ячейке ОЗУ данные из  

адреса #5CF4 / 5CF5 (сектор/дорожка), и можно приступать к загрузке тела игровой 

программы (можно сделать еще проще, если изначально все данные для файла 

состояния поместить в БЕЙСИК файле в НУЛЕВОЙ строке. Там же поместить и 

программу-опеделитель параметров файла).

     В теле игровой программы нами тоже должны быть внесены изменения.

Вместо обращения программы к магнитофону, мы должны вписать подпрограмму 

загрузки/выгрузки на диск, опираясь на сохраненные данные о секторе и о дорожке 

нашего файла. Используя все те принципы, которые используются при загрузке 

файлов (см.выше), несложно написать коротенькую подпрограмму, которая используя

функцию #06, будет сохранять состояние развития Вашей игры, конкретно в уже 

зафиксированный файл. Считывать тоже.

     При  неоднократном запуске программы с такой адаптацией, если на запрос  

"FILE ACCES?" Вы введете имя файла, который уже существует, то ТРДОС 

проигнорирует строку 30 БЕЙСИКА, т.к. файл с таким именем уже существует.

Но при этом в системной области ТРДОС оставит 'слепок'- имя и тип файла, и 

в основную программу, в конечном итоге, попадут координаты 'старого' файла, в  

котором сохранено состояние от предыдущего раза.

     Таким образом, "малой кровью" мы получим нужный результат. Правда, в таком 

методе есть один недостаток - невозможность сохранения предыдущих данных,

так как новые данные будут постоянно затирать старые. Но эту неприятность можно 

избежать простым копированием файлов предыдущих состояний на резервные диски и 

их переименованием.

     По такой, приблизительно, схеме адаптирована программа "ELITE" (адаптация  

"JOYSTICK CLUB") с сохранением боевого рейтинга пилота и остальных данных 

полета. Правда, там не используются ФУНКЦИИ ТРДОС, а программируется  

непосредственно контроллер дисковода. Но, в общем и целом, принципы сохранения 

состояния игры похожи.

                      * * *

Теперь, опираясь на все те знания, которые Вы уже почерпнули, Вы сможете 

адаптировать практически ЛЮБУЮ программу с кассеты на диск.

     Нетерпеливый читатель наверное воскликнет:

         "А на кой черт вся остальная книга?".

     Не торопитесь!!

Дело в том, что все самые интересные и сложные программы (а особенно, когда 

программы активно работают с диском), адаптируются методами, позволяющими  

непосредственно управлять дисковым накопителем, что гораздо экономичнее,

профессиональнее и удобнее. Правда, это намного сложнее, но!.. Как говорили

древние греки,-"Чем сложнее, тем проще!"

                 И вот тем,

        кто не успокоился на достигнутом,

            кто желает научиться: 

   - влезать в системную область диска, которую обычными методами не "достать";

   - форматировать диски не только стандартным способом;

   - каким образом прочитать диски других форматов (например от IBM PC);

   - как востанавливаются сбойные диски

             и многое-многое другое ...

     вот им то и предназначена оставшаяся часть книги.











55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555

TR-DOS для начинающих - 5-я глава книги "Общие сведения по дисковой системе ТР ДОС".

<b>TR-DOS для начинающих</b> - 5-я глава книги 

                     TR DOS ДЛЯ НАЧИНАЮЩИХ    



      В наступившем году мы продолжаем публикацию книги В.Сироткина

"Общие сведения по дисковой системе ТР ДОС". Нужно сказать, что публикуемые

далее материалы представляют интерес не только для начинающих, но и для 

более подготовленных пользователей и программистов. Поэтому название рубрики

уже не в полной мере отражает ее содержание.

                 

Продолжение. Начало см. в ZX РЕВЮ 1996 NN 1-2, 4-5, 6, 7-8                      

                      

                      ГЛАВА  5


      ИНТЕГРАЛЬНАЯ МИКРОСХЕМА КР 1818 ВГ93.

   --------------------------------------------

    Прежде всего нам надо разобраться с самой главной микросхемой контроллера 

дисковода ТРДОС (BETA DISK интерфейса): с микросхемой КР 1818 ВГ93.

Вся  логика работы системы зависит,прежде всего от нее. Эта микросхема 

представляет собой программный микроавтомат с внутренней логикой работы, 

который обеспечивает управление дисководом, обмен данными между процессором и  

дисководом, программирование номеров дорожки, сектора, стороны диска, длины 

сектора и т.д. и т.п.

     В общем, он обеспечивает все то ,что необходимо для нормальной работы с 

дисководом. Обмен данными между микросхемой ВГ93 и процессором копьютера 

происходит по 8-ми разрядной шине данных. По этой же шине происходит  

программирование микросхемы на различные режимы и подача команд контроллеру.

     Графическое  изображение  микросхемы показано ниже на рисунке 9.


                                 РИС 9.


Назначение выводов микросхемы.

------------------------------

ВЫВОД ! МНЕМОН.! НАЗНАЧЕНИЕ ВЫВОДА

--------------------------------------------------

 1    !   ВS   ! Вывод микросхемы контроль подлож-

      !        !   ки; в схемах не подключается.

--------------------------------------------------

 2    !   W    ! Вход ЗАПИСЬ.При W=0 - Разрешение

      !        !  записи  в выбранный регистр.

--------------------------------------------------

 3    !   CS   ! ВХОД. При 0 - микросхема выбрана

--------------------------------------------------

 4    !   R    ! Вход ЧТЕНИЕ.При = 0 - микросхема

      !        !  выдает нa Ш.Д. содержимое выбран-

      !        !  ного регистра .

--------------------------------------------------

 5,6  ! А0,А1  !Вход Адреса выбора внутренних

      !        ! регистров ВГ93 для операций чтения

      !        ! /записи.

      !        !

      !        !   А0  А1   ЧТЕНИЕ      ЗАПИСЬ

      !        !   -------------------------------

      !        !   0     0  РЕГ.СОСТ.   РЕГ.КОМАНД

      !        !   1     0  РЕГ.ДОРОЖ.  РЕГ.ДОРОЖ.

      !        !   0     1  РЕГ.СЕКТОР. РЕГ.СЕКТР.

      !        !   1     1  РЕГ.ДАННЫХ  РЕГ.ДАННЫХ

--------------------------------------------------

 7-14 ! D0-D7  ! Вход/Выход данных или команд

--------------------------------------------------

 15   ! STEP   ! Выход на дисковод импульса переме-

      !        ! щения головки на 1 шаг ( цилиндр)

--------------------------------------------------

 16   ! DIRC   ! Выход для дисковода. При =

      !        !  0 - перемещение головки от ЦЕТРА

      !        !      к краю ;

      !        !  при = 1 -  от края к центру

--------------------------------------------------

 17   ! SL     ! Выход для внешнего регистра .Сдвиг

      !        ! импульса данных с выхода WD ВЛЕВО.

--------------------------------------------------

 18   ! SR     ! Выход для внешнего регистра.Сдвиг

      !        ! импульса данных с выхода WD ВПРАВО.

--------------------------------------------------

 19   ! CLR    ! Вход СБРОСА. При =0 - микросхема

      !        ! выполняе команду ВОСТАНОВЛЕНИЕ.

      !        ! РЕГ.СЕКТОРА сбрасывается в #01.

      !        ! Выход 39 (INTRQ) = 0.

--------------------------------------------------

 20   ! GND    ! Корпусной вывод.

--------------------------------------------------

 21   ! Ucc1   ! Питание  + 5 вольт

--------------------------------------------------

 22   ! TEST   !Вход- Скорость перемещения головки.

      !        !            При =1

      !        !на выходе 'STEP' импульсы перемеще-

      !        !   ния идит с удвоенной частотой.

--------------------------------------------------

 23   ! HRDY   ! Вход ГОТОВНОСТЬ головки. При = 1

                  указание на готовность к работе.

--------------------------------------------------

 24   ! CLC    ! Тактовая частота

--------------------------------------------------

 25   ! RSTB   ! Выход СТРОБ ЧТЕНИЯ. Устанавлива-

      !        !  ется в 1 после приема 2х байтов

      !        !  нулей при одинарной плотности

      !        !  диска ;    или

      !        !  4х байтов нулей (единиц) при

      !        !  удвоенной плотности диска.

--------------------------------------------------

 26   !   S    ! Вход синхронизации. Сигнал выра-

      !        !  батывается из сигналов RAWR .

--------------------------------------------------

 27   ! RAWR   ! Вход ДАННЫХ от дисковода.

--------------------------------------------------

 28   ! HLD    ! Выход ГОТОВНОСТЬ для дисковода.

      !        !      (При =1 - готов)

--------------------------------------------------

 29   ! TR43   ! Выход  ПОЛОЖЕНИЕ головки.

      !        !  При = 1 указывает,что головка

      !        ! на цилиндре с номером большим 43.

      !        ! (Сигнал вырабатывается только в

      !        !   процессе ЗАПИСИ или ЧТЕНИЯ )

--------------------------------------------------

 30   ! WSTB   ! Выход. При = 1 включает дисковод

      !        !        на запись.

--------------------------------------------------

 31   ! WD     ! Выход данных для записи на диск.

--------------------------------------------------

 32   ! CPRDY  ! Вход Готовность дисковода для

      !        !       ЗАПИСИ или ЧТЕНИЯ.

      !        !     При = 0 - команды

      !        ! ЗАПИСЬ/СЧИТЫВАНИЕ НЕ ВЫПОЛНЯЮТСЯ,

      !        !  а вырабатывается СИГНАЛ INTRQ.

      !        ! (Не влияет на остальные вспомо-

      !        !      гательные команды).

--------------------------------------------------

 33   !WF / DE ! ВХОД / ВЫХОД : ОШИБКА / ДАННЫЕ .

      !        ! При  WSTB = 1 - WF - ВХОД;

      !        !  (если WF =0 то, запись любой ко-

      !        !    манды прекращается).

      !        ! При WSTB = 0 - DE - ВЫХОД;

      !        !  (на DE устанавливается 0 при

      !        !    ЧТЕНИИ после загрузки головки

      !        !    и HRDY = 1).

--------------------------------------------------

 34   ! TR00   ! ВХОД - НОЛЬ ДОРОЖКА.

      !        !  При = 0,головки дисковода нахо-

      !        !  дятся на 0 дорожке.

--------------------------------------------------

 35   ! JP     ! Вход ИНДЕКСЫЙ ИМПУЛЬС.

      !        ! При = 0 - индексный импульс счи-

      !        ! тан и диск начал очередной оборот.

--------------------------------------------------

 36   ! WPRT   ! Вход ЗАПРЕТ ЗАПИСИ . (При = 0)

--------------------------------------------------

 37   ! DDEN   ! Вход ПЛОТНОСТЬ ДИСКА.

      !        ! (При = 1 - плотность ДВОЙНАЯ)

--------------------------------------------------

 38   ! DRQ    ! Выход СТРОБ ДАННЫХ.

      !        ! *         При DRQ = 1 ;

      !        ! во время ЧТЕНИЯ - указание на то,

      !        ! что РЕГИСТР ДАННЫХ содержит инфор-

      !        ! мацию для передачи.

      !        ! Во время ЗАПИСИ - ГОТОВНОСТЬ реги-

      !        !  стра ВГ 93 принять данные от Ш.Д.

      !        ! *         При DRQ=0 ;

      !        ! При ЧТЕНИИ - указание,что данные

      !        ! из регистра ВГ93 считаны процес-

      !        ! сором.

      !        ! При ЗАПИСИ указание ,что регистр

      !        !  ВГ93 принял данные с Ш.Д.

--------------------------------------------------

 39   ! INTRQ  ! Выход ГОТОВНОСТЬ микросхемы.

      !        ! При = 1 - микросхема готова

      !        !   принять команду ;

      !        ! при = 0 - микросхема ВЫПОЛНЯЕТ

      !        !   команду.

      !        ! ИЛИ был считан регистр СОСТОЯНИЯ.

--------------------------------------------------

 40   ! Ucc 2  ! Напряжение питания +12 вольт.

--------------------------------------------------


     Для нас с вами не потребуется досконального знания функционирования всех 

входов-выходов микросхемы и режимов ее работы.

     С точки зрения программиста, нам потребуется знание логики работы всего  

лишь нескольких  выводов, подключенных непосредственно в адресное пространство 

портов контроллера ТР ДОС.

     Микросхема ВГ93 имеет 5 внутренних регистров, доступных программисту, 

через которые идет управление работой  ВГ93, и еще один внешний регистр,

выполненный, как правило, на микросхеме 555ТМ9 (триггере-защелке), для 

управления дисководом и для опроса готовности.

     Все регистры подключены к определенным адресам в адресном пространстве 

компьютера, как ПОРТЫ  ввода/вывода, и, значит, обращатьсяк к ним надлежит 

командами IN или OUT. Но простой подачей в эти порты каких-либо значений или 

при попытке считать что-то из этих портов, Вы ничего не добьетесь, т.к. эти 

порты во время работы SOS Бейсика недоступны!

     В ПЗУ ТРДОС есть все необходимые команды обращения к этим портам, и 

проблема заключается только в том, как выйти на ПЗУ ТРДОС. Вот об этом мы и 

поговорим чуть позднее...


        РАСПРЕДЕЛЕНИЕ ПОРТОВ ТРДОС - ВГ93.

        ----------------------------------


* РЕГИСТР ДОРОЖКИ - ПОРТ #3F (ЧТЕНИЕ/ЗАПИСЬ)

--------------------------------------------

Служит для ЗАПИСИ номера дорожки при операциях или для СЧИТЫВАНИЯ номера 

текущей дорожки.


* РЕГИСТР СЕКТОРА - ПОРТ #5F (ЧТЕНИЕ/ЗАПИСЬ)

--------------------------------------------

Служит для ЗАПИСИ номера сектора при операциях или для СЧИТЫВАНИЯ номера 

текущего сектора.


* РЕГИСТР ДАННЫХ  - ПОРТ #7F (ЧТЕНИЕ/ЗАПИСЬ)

--------------------------------------------

Служит для обмена данными между диском и компьютером.


* РЕГИСТР КОМАНД  - ПОРТ #1F ( ЗАПИСЬ )

--------------------------------------------

Служит для подачи команд контроллеру.


* РЕГИСТР СОСТОЯНИЯ - ПОРТ #1F ( СЧИТЫВАНИЕ )

--------------------------------------------

Служит для определения текущего состояния выполнения команд.


*  РЕГИСТР  УПРАВЛЕНИЯ  - ПОРТ #FF

           (СЧИТЫВАНИЕ/ЗАПИСЬ)

--------------------------------------------------

Служит  для управления дисководом и для считывания готовности микросхемы  ВГ93  

- при работе или при обмене данными. Этот регистр выполнен на микросхеме

ТМ9 - триггере защелке. При подаче в порт байта, он сохраняется - 

защелкивается.


     Рассмотрим подробнее РЕГИСТР УПРАВЛЕНИЯ (#FF).


**  При ЗАПИСИ в этот регистр можно выбрать номер дисковода, сбросить 

микросхему ВГ93, выбрать сторону диска, дать готовность дисководу и выбрать 

плотность записи.

    Ниже на рисунке представлены биты этого регистра и за что они отвечают при 

ЗАПИСИ в него.


    Режим ЗАПИСИ в регистр УПРАВЛЕНИЯ (#FF).

  ┌────┬─────┬─────┬──────┬─────┬──────┬─────┬─────┐                                                   

  │ 7  │  6  │  5  │   4  │   3 │   2  │   1 │   0 │                                                   

  │    │     │     │      │     │      │     │     │                                                   

  └────┴─────┴─────┴──────┴─────┴──────┴─────┴─────┘                                                   

                                                                                                

         │           │     │       │     ├────────┴───                                                   

         │           │     │       │     │выбор диска                                                   

         │           │     │       │     │при = 0,0                                                   

         │           │     │       │     └─диск 'А────                                                   

         │           │     │       │                                                   

         │           │     │       ├──────────────────                                                   

         │           │     │       │сброс микросхемы                                                   

         │           │     │       └───ВГ93 ; при=0───                                                   

         │           │     ├──────────────────────────                                                   

         │           │     │ готовность дисководу (=1)                                                   

         │           │     └──────────────────────────                                                   

         │           ├────────────────────────────────                                                   

         │           │ сторона диска  при= 1 - верх                                                   

         │           │                при= 0 - низ                                                   

         │           └────────────────────────────────                                                   

         │                                                   

         ├────────────────────────────────────────────                                                   

         │ плотность записи   при=1 - одинарная                                                   

         │                    при=0 - двойная                                                   

         └────────────────────────────────────────────                                                   



БИТЫ 0 и 1 - с выхода триггера ТМ9 напрямую связаны со входами дисковода "DISK 

             A,B,C"


БИТ  2     - это вход CLR микросхемы ВГ93


БИТ  3     - это вход HRDY микросхемы ВГ93 и одновременно разрешение 

             прохождения сигнала IP от дисковода к микросхеме ВГ93


БИТ  4     - напрямую связан со входом дисковода "SIDE"


BIT  6     - это вход  DDEN микросхемы ВГ93


     В  Режиме ЧТЕНИЕ регистра #FF использованы всего 2 бита, (смотри ниже).


     Режим ЧТЕНИЯ  регистра УПРАВЛЕНИЯ (#FF).                                                  

   ┌────┬─────┬─────┬──────┬──────┬──────┬──────┬────┐                                                  

   │    │     │     │      │      │      │      │    │                                                  

   │ 7  │  6  │  5  │   4  │   3  │   2  │   1  │  0 │                                                  

   └────┴─────┴─────┴──────┴──────┴──────┴──────┴────┘                                                  

     │     │    │═══════ НЕ ИСПОЛЬЗУЕТСЯ ═════════│                                                  

     │     │                                                  

     │   ┌─┴──────────┬────────────────────────────────                                                  

     │   │ готовность │ при= 1 - готовность к обмену                                                  

     │   │ ДАННЫХ:    │ при= 0 - данные считаны/приняты                                                  

     │   └────────────┴────────────────(не готовы )────                                                  

     │                                                  

     ├─────────────────────────────────────────────────                                                  

     │ готовность         при=1 - команда ВЫПОЛНЕНА                                                  

     │ МИКРОСХЕМЫ ВГ93:   при=0 - команда на ИСПОЛНЕНИИ                                                  

     │                            или считан регистр                                                  

     └─────────────────────────────────СОСТОЯНИЯ ВГ93──                                                  

                                                        

БИТ 6  - это выход  DRQ   микросхемы ВГ93

БИТ 7  - это выход  INTRQ микросхемы ВГ93


     Микросхема ВГ93 обеспечивает прием и выполнение 11 команд, поданных в 

Регистр КОМАНД. Каждая команда,из одиннадцати, имеет свой КОД-МОДИФИКАТОР , 

который изменяет некоторые условия выполнения команды.

     Условно команды подразделяются на четыре типа:

1 - команды перемещения головок дисковода и поиска: эти команды загружают 

(прижимают) головки.

2 - команды чтения/записи секторов диска.

3 - команды чтения/записи дорожки и адресной метки.

4 - команды принудительного прерывания операций.

     Результаты выполнения или невыполнения команд отображаются в Регистре 

СОСТОЯНИЯ, каждый бит которого несет информацию о ходе выполнения команды.


     Разберем подробнее каждую команду.

            Команды ПЕРВОГО ТИПА.

          -------------------------

    Данные команды обеспечивают загрузку головок, т.е. прижатие их к поверхности 

диска.

*  Команда   ВОСТАНОВЛЕНИЕ.

............................

  Переход головок дисковода на НУЛЕВОЙ ТРЕК (ЦИЛИНДР). Если на входе TR00 

микросхемы не появится подтверждение о выходе головок на 0 ,то через 256

импульсов действие команды прекращается.


   КОД команды.

                                                      

  ┌─7──┬──6──┬──5──┬───4──┬───3──┬───2──┬───1──┬──0─┐                                                  

  │    │     │     │      │      │      │      │    │                                                  

  │ 0  │  0  │  0  │   0  │  G   │  V   │  F1  │ F2 │                                                  

  └────┴─────┴─────┴──────┼──────┴──────┴──────┴────┘                                                  

                          │  МОДИФИКАТОР                                                  

При:

G=0 - головка поднята.

G=1 - головка опущена (в работе).

V=0 - местонахождение головки не проверяется.

V=1 - читается и проверяется номер цилиндра под головкой.

F1,F2 - коды времени перемещения головок (см.таблицу).


* KОМАНДА ПОИСК ЦИЛИНДРА.

..........................

   КОД команды.


┌─7──┬──6──┬──5──┬───4──┬───3──┬───2──┬──1──┬───0──┐

│    │     │     │      │      │      │     │      │

│ 0  │  0  │  0  │   1  │  G   │  V   │ F1  │  F2  │

└────┴─────┴─────┴──────┼──────┴──────┴─────┴──────┘

                        │  МОДИФИКАТОР

При:

G=0 - головка поднята.

G=1 - головка опущена (в работе).

V=0 - местонахождение головки не проверяется.

V=1 - читается и проверяется номер цилиндра под головкой.

F1,F2 - коды времени перемещения головок (см.таблицу).

     Перед  командой  Регистр  ДОРОЖКИ должен содержать номер ТЕКУЩЕГО ЦИЛИНДРА, 

а Регистр ДАННЫХ номер ТРЕБУЕМОГО. Поиск продолжается до тех пор, пока регистр 

ДОРОЖКИ не сравняется с Регистром ДАННЫХ.

     Поиск выполняется при модификаторе V=1 !!


* КОМАНДА  ШАГ.

................

    Перемещение головки на ОДИН шаг,(на 1 цилиндр). Направление перемещения 

остается прежним.


   КОД команды.          

                         

┌─7──┬──6──┬──5──┬───4──┼───3──┬───2──┬──1──┬───0──┐

│    │     │     │      │      │      │     │      │

│ 0  │  0  │  1  │   J  │  G   │  V   │ F1  │  F2  │

└────┴─────┴─────┼──────┴──────┴──────┴─────┴──────┘

                 │      МОДИФИКАТОР

При:

G=0 - головка поднята.

G=1 - головка опущена (в работе).

V=0 - местонахождение головки не проверяется.

V=1 - читается и проверяется номер цилиндра под головкой

F1,F2 - коды времени перемещения головок (см.таблицу).

J=0  - содержимое Регистра ДОРОЖКИ не меняется.

J=1  - содержимое Р.ДОРОЖ. меняется на 1 (+ /-).


* КОМАНДА  ШАГ ВПЕРЕД (к центру диска)

......................................

Перемещение головки на ОДИН шаг ВПЕРЕД  (на 1 цилиндр).


   КОД команды.

                        │

┌─7──┬──6──┬──5──┬───4──┼───3──┬───2──┬──1──┬───0──┐

│    │     │     │      │      │      │     │      │

│ 0  │  1  │  0  │   J  │  G   │  V   │ F1  │  F2  │

└────┴─────┴─────┼──────┴──────┴──────┴─────┴──────┘

                 │      МОДИФИКАТОР


При:

G=0 - головка поднята.

G=1 - головка опущена (в работе).

V=0 - местонахождение головки не проверяется.

V=1 - читается и проверяется номер цилиндра под головкой.

F1,F2 - коды времени перемещения головок (см.таблицу).

J=0  - содержимое Регистра ДОРОЖКИ не меняется.

J=1  - содержимое Р.ДОРОЖ. увеличивается на 1.


* КОМАНДА ШАГ НАЗАД (к краю диска)

......................................

Перемещение головки на ОДИН шаг НАЗАД (на 1 цилиндр).


   КОД команды.


┌─7──┬──6──┬──5──┬───4──┼───3──┬───2──┬──1──┬───0──┐

│    │     │     │      │      │      │     │      │

│ 0  │  1  │  1  │   J  │  G   │  V   │ F1  │  F2  │

└────┴─────┴─────┼──────┴──────┴──────┴─────┴──────┘

                 │      МОДИФИКАТОР


При:

G=0 - головка поднята.

G=1 - головка опущена (в работе).

V=0 - местонахождение головки не проверяется.

V=1 - читается и проверяется номер цилиндра под головкой.

F1,F2 - коды времени перемещения головок (см.таблицу).

J=0  - содержимое Регистра ДОРОЖКИ не меняется.

J=1  - содержимое Р.ДОРОЖ. уменьшается на 1.


               КОМАНДЫ ВТОРОГО ТИПА.

           -----------------------------

* КОМАНДА ЧТЕНИЕ  СЕКТОРА ,(секторов).

......................................

   Команда выполняется после того, как микросхемой ВГ93 идентифицирована  

область ИАМ сектора (индексная адресная метка) и подсчитана контрольная сумма

(все это происходит автоматически после подачи команды и не зависит от 

программиста).

   Перед командой необходимо:

           1. Установить головку на нужный ЦИЛИНДР

           2. в Регистр СЕКТОРА занести номер требуемого СЕКТОРА.

   КОД команды.

┌─7──┬──6──┬──5──┬───4──┼───3──┬───2──┬───1──┬───0──┐

│    │     │     │      │      │      │      │      │

│ 1  │  0  │  0  │   М  │  S   │  E   │  C   │  0   │

└────┴─────┴─────┼──────┴──────┴──────┴──────┴──────┘

                 │      МОДИФИКАТОР


При:

M=0  -  чтение ОДНОГО сектора, содержимое Регистра СЕКТОРА увеличивается на 1.

М=1  -  после чтения одного сектора, содержимое Регистра СЕКТОРА увеличивается  

        на 1, и начинается чтение следующего - и так до последнего сектора на 

        ДОРОЖКЕ.

S=0 -   сторона диска НИЗ.

S=1 -   сторона диска ВЕРХ.

Е=0 -   нет задержки для установки головки дисковода.

Е=1 -   задержка в 15 мс. для установки головки после сигнала HLD микросхемы 

        (готовность).

С=0 -   сторона диска не проверяется.

С=1 -   проверка стороны диска в процессе идентификации.


     Если в процессе идентификации микросхема не находит в ИАМ-области НОМЕР 

нужного СЕКТОРА, или же вообще не находит ИАМ, то в Регистре СОСТОЯНИЯ 

записывается признак "МАССИВ ЧТЕНИЯ НЕ НАЙДЕН".

     Если по окончании считывания сектора не совпали Контрольные суммы, то в 

Регистр СОСТОЯНИЯ записывается признак ОШИБКА, и выполнение команды ОБРЫВАЕТСЯ.


     При чтении массива данных из поля сектора в Регистр ДАННЫХ микросхемы,

КАЖДЫЙ байт сопровождается сигналом СТРОБ ДАННЫХ (вывод DRQ микросхемы;

6-й бит порта #FF, контроллера ТРДОС).


     Если текущий байт не был считан из Регистра ДАННЫХ по сигналу СТРОБ ДАННЫХ

до прихода следующего байта, то в регистр ДАННЫХ записывается следующий

байт, а в Регистре СОСТОЯНИЯ записывается признак ПОТЕРЯ ДАННЫХ.


* КОМАНДА  ЗАПИСЬ СЕКТОРА ,(секторов).

......................................

   Команда выполняется после того, как микросхемой ВГ93 идентифицирована 

область ИАМ сектора, (индексная адресная метка) и подсчитана контрольная

сумма (все это происходит автоматически после подачи команды и не зависит от 

программиста).

    Перед командой необходимо:

    1. Установить головку на нужный ЦИЛИНДР.

    2. в Регистр СЕКТОРА занести номер требуемого СЕКТОРА.


   КОД команды.

┌─7──┬──6──┬──5──┬───4──┼───3──┬───2──┬───1──┬──0──┐

│    │     │     │      │      │      │      │     │

│ 1  │  0  │  1  │   М  │  S   │  E   │  C   │ А   │

└────┴─────┴─────┼──────┴──────┴──────┴──────┴─────┘

                 │      МОДИФИКАТОР

При:

M=0 - запись ОДНОГО сектора, содержимое Регистра СЕКТОРА увеличивается на 1.

М=1 - после записи одного сектора,содержимое Регистра СЕКТОРА увеличивается 

      на 1 и начинается запись следующего, и так до последнего сектора на 

      ДОРОЖКЕ.

S=0 - сторона диска НИЗ.

S=1 - сторона диска ВЕРХ.

Е=0 - нет задержки для установки головки дисковода.

Е=1 - задержка в 15 мс. для установки головки после сигнала HLD микросхемы 

      (готовность).

С=0 - сторона диска не проверяется.

С=1 - проверка стороны диска в процессе идентификации.

А=0 - запись в область ИАМ признака - "сохранения данных (#FB) (без стирания)".

A=1 - запись в ИАМ признака - "стирать данные разрешено (#F8)".


     Если в процессе идентификации микросхема не находит в ИАМ области НОМЕР 

нужного СЕКТОРА, или же вообще не находит ИАМ, то в Регистре СОСТОЯНИЯ 

записывается признак МАССИВ ЧТЕНИЯ НЕ НАЙДЕН.


     После того, как ИАМ найдена, следует запрос данных - сигнал DRQ. 

Микросхемой записываются (автоматически) нужные пробелы в секторе (12 нулей) 

перед областью данных, затем записывается "Адресная метка данных" и только

затем сами данные.

     Перед каждым запросом следующего байта данных микросхемой выставляется 

Строб ДАННЫХ, (выход DRQ).

     Если на запрос DRQ в регистр данных не будет помещен следующий байт для 

записи, то в регистре СОСТОЯНИЯ  выставится  признак ПОТЕРЯ ДАННЫХ, а на

диск запишется байт 00 !

     После записи всего массива данных микросхемой автоматически записывается 

контрольная сумма в 2 байта и байт #FF.


              KОМАНДЫ ТРЕТЬЕГО ТИПА.

              ----------------------

* КОМАНДА  ЧТЕНИЯ АДРЕСНОЙ МЕТКИ .

...................................

     Команда выполняется тогда, когда головка дисковода находится в рабочем 

положении, т.е. прижата к диску (сигнал HLD=1). Считываются ШЕСТЬ байтов 

ИНДЕКСНОЙ области диска, включая Контрольную сумму (Номер цилиндра, сторона  

диска, тип сектора, номер сектора, 2 байта контр.суммы).

     Все байты сопровождаются СТРОБОМ чтения БАЙТА (вывод DRQ или 6-й бит порта 

#FF). При выполнении команды содержимое Регистра ДОРОЖКИ пересылается в

Регистр СЕКТОРА и запоминается.


     По окончании выполнения команды генерируется сигнал INTRQ (7 бит порта 

#FF).

   КОД команды.         │

┌─7──┬──6──┬──5──┬───4──┼───3──┬───2──┬───1──┬──0─┐

│    │     │     │      │      │      │      │    │

│ 1  │  1  │  0  │   0  │  0   │  E   │  0   │  0 │

└────┴─────┴─────┴──────┴──────┴─┬────┴──────┴────┘

                                 │

                                 - МОДИФИКАТОР

При:

Е=0 - нет задержки для установки головки дисковода.

Е=1 - задержка в 15 мс. для установки головки после сигнала HLD микросхемы 

      (готовность).


* КОМАНДА: ЧТЕНИЕ ДОРОЖКИ ЦЕЛИКОМ.

.................................

     По  этой  команде считывается ВСЯ информация с дорожки, вместе с индексным 

массивом,пробелами и областью  данных.  Во время чтения НЕ выдается СТРОБ

чтения  БАЙТА и не проверяются контрольные суммы. Применяется, в основном, эта 

команда в диагностических целях, например, для юстировки головок дисковода или 

в программах типа "ТРЕК КОПИР".


   КОД команды.

┌─7──┬──6──┬──5──┬───4──┼───3──┬───2──┬───1──┬─0─┐

│    │     │     │      │      │      │      │   │

│ 1  │  1  │  1  │   0  │  0   │  E   │  0   │  0│

└────┴─────┴─────┴──────┴──────┴─┬────┴──────┴───┘

                                 │

                                 - МОДИФИКАТОР


При:

Е=0 - нет задержки для установки головки дисковода.

Е=1 - задержка в 15 мс. для установки головки после сигнала HLD микросхемы 

      (готовность).


* КОМАНДА: ЗАПИСЬ ДОРОЖКИ ЦЕЛИКОМ.

..................................

     Эта  команда применяется для ФОРМАТИРОВАНИЯ диска. Запись информации 

происходит за ОДИН РАЗ на целую дорожку. При форматировании вся необходимая  

информация должна находиться в ОЗУ/ПЗУ и содержать все пробелы и индексные 

метки.


   КОД команды.

┌─7──┬──6──┬──5──┬───4──┼───3──┬───2──┬───1──┬───0─┐

│    │     │     │      │      │      │      │     │

│ 1  │  1  │  1  │   1  │  0   │  E   │  0   │  0  │

└────┴─────┴─────┴──────┴──────┴─┬────┴──────┴─────┘

                                 │

                                 - МОДИФИКАТОР

При:

Е=0 - нет задержки для установки головки дисковода.

Е=1 - задержка в 15 мс. для установки головки после сигнала HLD микросхемы 

      (готовность).

     При выполнении команды ЛЮБАЯ информация запишется на дорожку, кроме 

некоторых байтов. А именно: появление  байтов  #F5...#FE интерпретируются как

начало СЛУЖЕБНЫХ МЕТОК. БАЙТ #F5 - запись кода #А1, инициализация подсчета 

контрольной суммы.

БАЙТ #F6 - запись кода С2 - индексная метка.

БАЙТ #F7 - запись контрольной суммы.

БАЙТ #FB - адресная метка данных.

БАЙТ #FE - адресная метка ИНДЕКСНЫХ данных.

     Все это справедливо для записи на диск ДВОЙНОЙ плотности (с 

модифицированной частотной мадуляцией). Для одинарной плотности байты меток 

будут немножко другими, но такие дисководы стали уже редкостью и рассматривать 

одинарную плотность записи мы не будем.

     В процессе форматирования эти байты меток не должны записываться в область 

пробелов и в область для данных! Подробнее массив данных для форматирования 

дисков мы рассмотрим в отдельной главе.


             КОМАНДЫ ЧЕТВЕРТОГО ТИПА.

             ........................

* КОМАНДА "ПРИНУДИТЕЛЬНОЕ ПРЕРЫВАНИЕ".

     Команда предназначена для завершения выполняемой операции и может быть 

записана  в Регистр КОМАНД в любой момент времени.

                КОД команды.

┌─7──┬──6──┬──5──┬───4──┼───3──┬───2──┬───1──┬───0─┐

│    │     │     │      │      │      │      │     │

│ 1  │  1  │  0  │   1  │  m3  │  m2  │  m1  │  m0 │

└────┴─────┴─────┴──────┼──────┴──────┴──────┴─────┘

                        │      МОДИФИКАТОР

При:

m3=m2=m1=m0  =  0  команда прекращается, но сигнал JNTRQ не вырабатывается 

                (порт #FF, 7 байт).

м0  = 1 прерывание происходит при переходе от 0 на 1 на входе CPRDY.

m1  = 1 прерывание происходит при переходе от 1 на 0 на входе CPRDY.

m2  = 1  прерывание по первому индексному сигналу (началу нового оборота диска).

м3 =  1 немедленное прерывание и установка на выходе JNTRQ сигнала ГОТОВНОСТЬ 

      (бит 7, порта #FF).

     Время установки времени перемещения головки в командах ПЕРВОГО типа 

зависит, прежде всего, от уровня на входе 'TEST' микросхемы ВГ93, тактовых 

импульсов на входе CLK, а также от состояния модификаторов команд #F0,#F1.


__________________________________________________

TEST  !   F1  !   F0  !Время перемещения на 1 шаг

      !       !       !при CLK=1 MГЦ  ! при CLK=2

--------------------------------------------------

  1   !    0  !   0   !    6          !     3

--------------------------------------------------

  1   !    0  !   1   !    12         !     6

--------------------------------------------------

  1   !    1  !   0   !    20         !     10

--------------------------------------------------

  1   !    1  !   1   !    30         !     15

--------------------------------------------------

  0   !    -  !   -   !    400        !     200

__________________________________________________


     Любая команда в процессе своего исполнения обновляет регистр СОСТОЯНИЯ  

микросхемы (порт #1F при чтении). С помощью этого регистра как раз и ведется 

"диалог" между компьютером и контроллером о выполнении или невыполнении команд.

     Ниже дана таблица значения битов этого регистра при выполнении различных 

команд. А следом за ней помещена таблица, в которой показаны все команды ВГ93.











 (C) Лебедев Павел, 1996

Недавно мне понадобилась как можно более подробная информация по

программированию КР1818ВГ93 и я полез в сборники ZX РЕВЮ... Вот тут-то все и

началось!

Сначала я хочу сказать о вызове подпрограмм TR-DOS. Время от времени в

различных книгах и программах вместо

                PUSH    HL

                JP      #3D2F

встречается

                PUSH    HL

                JP      #3D30


Хочу предостеречь людей от таких "улучшений". Несмотря на то, что по

адресу #3D2F в TR-DOS находится команда NOP, а RET - в следующей ячейке,

передавать управление необходимо именно на адрес #3D2F. Дело в том, что в

некоторых компьютерах ПЗУ не успевает переключаться достаточно быстро, и

процессор как бы "проскакивает" команду RET. Последствия этого могут быть

самыми печальными.

Теперь несколько слов об управлении КР181ВГ93. Практически во всех

изданиях, посвященных этому вопросу, сказано, что напрямую прочитать регистр

состояния из порта #1F невозможно. Каково же было мое удивление, когда

поковырявшись в своей TR-DOS 5.03 по адресу #09BF я обнаружил следующие

команды:

        09BF    IN      A,(#1F)

        09A1    RET

        Возможно, это имеет место только в TR-DOS 5.03.?

        В связи с этим, а также в целях создания более качественного

программного обеспечения для работы с КР1818ВГ93 программистами СНГ, предлагаю

открыть раздел, в который все желающие могли бы прислать адреса необходимых

подпрограмм для своей версии ПЗУ, чтобы их версия TR-DOS поддерживались новыми

программами.



        Предлагаю точки входа для TR-DOS 5.03:

        1. Вывод в порт BC регистра A:

                2A53    OUT     (C),A

                2A55    RET

        2. Вывод в порт #1F (регистр команд) регистра A:

                2FC3    OUT     (#1F),A

                2FC5    RET

        3. Вывод в порт #FF регистра A:

                1FF3    OUT     (#FF),A

                1FF5    RET

        4. Чтение регистра состояния (порт #1F) в регистр A:

                09BF    IN      A,(#1F)

                09A1    RET


(snd: в версии 5.04 пункты 1,2,3 совпадают...4 - в пзу 5.04 отсутствует!)



       И наконец - хочу привести усовершенствованную версию Advanced Drive

FX, опубликованого в ZX РЕВЮ 95/5. Новая версия несколько более понятна,

меньше по объему, позволяет оперировать со всеми дисководами от A до D и

изменять скорость зажигания и затухания светодиода. Для зажигания необходимо

вызвать процедуру F_IN, а для затухания - F_OUT. Подпрограммы работы с портами

настроены на TR-DOS 5.03. Для работы с другими версиями надо изменить адреса в

строках 390-410. В строке 420 регулируется скорость эффекта, а в строке 430

задается дисковод.

 10         ORG     50000

 20 F_IN    LD      E,1             ;зажигание производится при изменении

                                    ;переменной на +1

 30         JR      DO_FX           ;переход на процедуру

 40 F_OUT   LD      E,255           ;затухание производится при изменении

                                    ;переменной на -1

 50 DO_FX   XOR     A               ;подготовка аккумулятора

 60 LOOP1   ADD     A,E             ;изменяем переменную

 70         RET     Z               ;выход, если светодиод зажегся или потух

                                    ;полностью

 80         LD      B,SPEED         ;взяли скорость загорания (затухания)

 90 LOOP2   PUSH    BC              ;сохранили счетчик

100         LD      D,A             ;сохранили указатель загорания (затухания)

110         LD      HL,OUT_FF       ;адрес подпрограммы вывода регистра A в

                                    ;системный регистр (порт #FF)

120         LD      A,#3C           ;в аккумуляторе необходимое число, которое

                                    ;указывает на номер дисковода: биты 0 и 1.

130         CALL    DOS             ;заносим число в порт

140         LD      HL,OUT_1F       ;выдаем команду контроллеру "Чтение

                                    ;адреса"

150         LD      A,#D0

160         CALL    DOS

170 WAIT    LD      HL,IN_1F        ;чтение регистра состояния

180         CALL    DOS

190         XOR     #80             ;проверка готовности дисковода

200         JR      NZ,WAIT         ;если не готов, то ждать

210         LD      A,8             ;выдаем команду "Восстановление"

220         LD      HL,OUT_1F

230         CALL    DOS

240         LD      B,D             ;задержка

250         DJNZ    $

260         LD      HL,OUT_FF       ;выбираем нужный дисковод

270         LD      A,DRVNUM

280         CALL    DOS

290         LD      A,D             ;восстановили указатель

300         NEG                     ;изменили знак

310         LD      B,A             ;задержка

320         DJNZ    $

330         LD      A,D             ;восстановили указатель

340         POP     BC              ;восстановили счетчик

350         DJNZ    LOOP2           ;повтор, пока счетчик не обнулился

360         JR      LOOP1           ;переход на начало процедуры

370 DOS     PUSH    HL              ;процедура вызова подпрограмм из ПЗУ

                                    ;TR-DOS

380         JP      #3D2F

390 IN_1F   EQU     #09BF           ;адрес подпрограммы чтения системного

                                    ;регистра

                                    ;необходимо изменить под конкретный тип

                                    ;ПЗУ

400 OUT_1F  EQU     #2FC3           ;выдача команды для контроллера

410 OUT_FF  EQU     #1FF3           ;запись системного регистра

420 SPEED   EQU     #08             ;скорость эффектов

430 DRVNUM  EQU     #00             ;номер дисковода

        Не забудьте, что при использовании других версий TR-DOS надо изменить

адрес в строке 390. Для того, чтобы узнать, есть ли у Вас в ПЗУ такая

подпрограмма, необходимо сначала выгрузить ПЗУ TR-DOS на диск.












ИФК:  Павел  Ковалевский (Smith CODER) из г. Нижевартовска прислал программу,

которая позволяет считывать данные из системных портов DOS.

КОРР:  Во всех изданиях о TR-DOS я находил информацию только о записи данных

в  системные порты DOS, а о чтении их либо не говорилось, либо утверждалось, что

сделать  это практически невозможно. В принципе, это все правильно, но все же 

вытащить  данные  из  TR-DOS можно и довольно просто. Да, действительно, в DOS 

нет прямой  команды IN A,(C) и RET, и считать порт просто не получится. Но 

можно обмануть процессор, используя IM2, который генерирует прерывания 50 раз в

секунду.

Для этого ждем когда до генерации прерывания остается несколько тактов и 

передаем  управление  DOS в любую точку, где есть IN H,(C) или подобная команда.

После того, как процессор выполнит команду IN H,(C), он переходит на следующую,

вот тут-то происходит прерывание, и, в итоге, у Вас в регистре H данные из порта

#3F.

          ORG 25000

           LD IX,16115   ; АДРЕС КОМАНДЫ IN H,(C)

           LD BC,#3F     ; СИСТЕМНЫЙ РЕГИСТР

           CALL DOS

           RET

DOS       LD (COC+1),SP ; ЗАПОМИНАЕМ СТЕК

          PUSH IX       ;┐

          PUSH HL       ;│ЗАПОМИНАЕМ ИЗМЕНЯЕМЫЕ РЕГИСТРЫ

          PUSH DE       ;│

          PUSH AF       ;┘

          LD A,201

          LD (65535),A

          LD A,59       ; УСТАНАВЛИВАЕМ РЕЖИМ IM2 ДЛЯ ПРАВИЛЬНОГО ОТСЧЕТА

          LD I,A

          IM 2

          EI

          HALT          ; НАЧАТЬ ОТСЧЕТ

           DI

           LD HL,MET

           LD (60927),HL ; 237*256+255

           LD A,237      ; УСТАНАВЛИВАЕМ АДРЕС ДЛЯ ВОЗВРАТА В MET

           LD I,A

           IM 2

           EI

XOX       LD DE,2751    ; СЧЕТЧИК

AS        DEC DE

           LD A,D

           OR E

           JR NZ,AS

           POP AF        ;┐

           POP DE        ;│ВОССТАНАВЛИВАЕМ РЕГИСТРЫ КРОМЕ IX

           POP HL        ;┘

           JP 15664      ; ВЫХОД В DOS

MET       DI            ; ВОЗВРАТ. В РЕГИСТРЕ H ДАННЫЕ СИСТЕМНОГО РЕГИСТРА #3F

COC       LD SP,0

           IM 1

           EI

           RET







IM1 & 2

from Fyrex

Пункт 1 -> Проблема вектора прерываний

Всем известно, что для  эффективного  создания  любой  программы

нужно  использовать  прерывание  IM2,  но  иногда  используют не

совсем верный способ установки этих  прерываний.  Заранее  прошу

извинения  за  повторение  прописных истин, но для некоторых эти

истины не до конца  прописаны.  Так  вот,  в  классическом  виде

установка прерываний IM2 происходит следующим путём:


а) создание таблицы  прерываний;

б)  установка  указателя  на   подпрограмму прерывания;

в) запрещение текущих прерываний;

г) установка нового вектора таблицы прерываний;

д) переход в режим IM2;

е)  разрешение уже вновь установленных прерываний IM2.

 

В этом месте, конечно, многие скажут: "Да, все верно, так это  и

происходит"  -  но некоторые "экономы" сразу заметят то, что они

пропускают один или несколько из вышеперечисленных шагов.  Сразу

проясню,  что же происходит, если не совсем корректно установить

прерывания IM2. Все достаточно просто:  в  момент  возникновения

самого  прерывания,  переход программы происходит не совсем в ту

подпрограмму, которая задумана, и даже вовсе в  случайное  место

памяти.  Главный  нюанс  всей проблемы в том, что это происходит

далеко не на всех компьютерах и  не  всегда  одинаково.  А  суть

этого  кроется  в  не  совсем  отлаженной  плате компьютера, как

правило, самопального. Решение же этой  проблемы  заключается  в

правильном способе установки в своей программе прерывания IM2, и

тогда на любом, самом трухлявом Пентагоне, все  будет  нормально

работать.   Приведу   для  примера  кусок  программы,  правильно

ставящей IM2, и если кто-то не хочет  углубляться  далее  в  эту

проблему, просто скопируйте этот участок себе в код.

 

       LD   HL,#BE00

       LD   DE,#BE01

       LD   BC,#1BF

       LD   A,H

       LD   (HL),C

       LDIR

       LD   (HL),#C3

       LD   HL,MY_INT_ROUTINE

       LD   (#BFC0),HL

       DI

       LD   I,A

       IM   2

       EI

       ....

 

Здесь  мы  задействуем  под  прерывание  IM2   области   памяти:

#BE00..#BF01 и #BFBF..#BFC1.


По сути, этот участок и есть классическая  установка  IM2.  Чаще

всего  некоторым кодерам не хочется жертвовать 257-ю байтами для

полной таблицы прерываний, и они устанавливают указатель таблицы

в различные места - кто куда придумает, в основном им полюбилась

область ПЗУ, из-за этого обычно и происходят фатальные зависы их

программ у некоторых юзеров.


Вот  вам  первый  постулат:


*  Нежелательно  устанавливать  вектор  прерываний  вне  области

памяти   #8000..#BF00.   В   области   памяти  #4000..#7f00  или

#C000..#FF00 не  стоит  ставить  вектор  таблицы,  поскольку  на

многих компьютерах с медленной памятью по этим адресам как раз и

располагается эта самая медленная память.  Фатальных  глюков  не

происходит,  но  сама  программа может работать заторможено. А в

области памяти #0000..#3F00  вообще  лучше  никогда  не  ставить

вектор  таблицы,  поскольку  там  невозможно  создать нормальную

таблицу прерываний.


А вот второй постулат:


*  При  создании  самой  таблицы  прерываний  корректнее   всего

заполнить всю таблицу (257 байт) одним и тем же значением.

 

Это, пожалуй, самое злосчастное место, ведь используя таблицу из

ПЗУ (т.е. заполненную мусором), или не заполняя её в ОЗУ, вы тем

самым допускаете вероятность перехода процессора в ложное  место

памяти, ведь никто со 100% вероятностью вам не гарантировал, что

переход  происходит  только  по  2  последним  байтам   (включая

Клайва).  Т.е.  я не говорю, что подобное явление носит массовый

характер, но, судя по нашему городу, могу утверждать, что с этим

часто  сталкивались  многие. Когда спек был больше распостранён,

да и программ делали больше, это явление встречалось часто, т.е.

то  у кого-то одно не работает, то у другого другое. Сейчас могу

точно сказать, что у одного из четырех  спектрумистов,  знакомых

мне  (включая  меня  :),  не  работает  ни  одна  из программ, с

хреновой таблицей прерываний. У меня, по странному капризу моего

Пентагона,  подобные программы могут проработать довольно долго,

но кончают  все  одинаково  -  сбросом  через  некоторое  время.

Напоследок приведу свой способ установки прерываний IM2, который

мне кажется удобней:

  

Где-то в начале программы:

  

       ORG  #8000

       DEFS 257,#81 ; в ALASMe заполняет 257 байт кодом #81

       ....

      

       ORG  #8181

       DI

       

       ; INTERRUPT ROUTINE

       

       EI

       RET

       ....

ENTRY:

       ....

       DI

       LD   A,#80

       LD   I,A

       IM   2

       EI

       ....

       

Таким образом таблица и  сопутствующие  действия  происходят  на

этапе   трансляции   программы,  что  избавляет  вас  от  лишних

действий, к тому  же  вы  как  бы  включаете  таблицу  в  размер

программы,  тем  самым  заранее  соглашаетесь с лишними занятыми

257-ю байтами, которые на самом деле никогда не были  решающими.

Кстати,  нетрудно  заметить,  что по адресу #8101 получаются 128

свободных  байт,  которые  можно  использовать  как  ячейки  для

переменных.   Необходимо   добавить,  что  если  в  конце  вашей

программы нужно передать управление системе,  то  лучше  сделать

это следующим способом:

 

       DI

       LD   A,#3F

       LD   I,A

       IM   1

       RET    (оооох ! чуть не написал rts ;-)

 

Почему это стоит делать именно таким образом, читайте далее.


   Пункт 2 -> Вектор прерываний и TR-DOS.

   

Создав  правильную  таблицу  прерываний  IM2  согласно   советам

предыдущего  пункта,  вы  ещё  не  до  конца  освобождаетесь  от

потенциальных проблем с прерываниями. Если вы захотите  вдруг  в

середине  программы,  где  уже  работает  ваше  прерывание  IM2,

загрузить что-нибудь с диска, то обычно  используете  для  этого

подпрограммы  TR-DOS.  Мы  сейчас  не рассматриваем загрузчики с

включенными  прерываниями  IM2,  это   уже   рассматривалось   в

различных  статьях. Следующий комментарий в равной степени будет

относиться и к стандартной работе с  TR-DOS  через  #3D13,  и  к

часто  используемым  быстрым  загрузчикам  (через  прямой  вызов

низкоуровневых подпрограмм  работы  с  ВГ93).  Перед  работой  с

TR-DOS  многие отключают музыку и прерывания посредством команды

DI.  Как  показывает  грустная  практика,  этого   недостаточно,

особенно  при  использовании  низкоуровневых подпрограмм TR-DOS.

Другими словами, перед вызовом любой подпрограммы  TR-DOS  лучше

перейти в режим прерывания IM1 c вектором #3F:


       DI

       LD   A,#3F

       LD   I,A

       IM   1

       CALL TR-DOS

       

Эта проблема очень похожа на описанную в предыдущем  пункте,  но

здесь  уже  происходит  конфликт  между  ВГ93 и процом. Особо не

углубляясь   в   детали,   советую   использовать    приведенную

конструкцию  для  доступа  в  TR-DOS.  При использовании быстрых

загрузчиков  с  использованием  низкоуровневых  вызовов   TR-DOS

хватит лишь добавления IM1/#3F в начало цикла счета секторов для

загрузки или записи.


   Пункт 3 -> Опрос клавиатуры.

  

Предполагаю, что к этому пункту вряд ли прислушаются, но не могу

не  поведать  о  такой  проблеме,  как опрос клавиатуры. Я думаю

многие не понаслышке  знакомы  с  таким  понятием,  как  дребезг

клавиатуры.  Я  с ним вплотную познакомился именно на Пентагоне.

Другими словами, при не очень  удачном  коде  опроса  клавиатуры

хрен  чего  наберешь  потом.  Сразу могу однозначно сказать, что

самый лучший способ опросить клавиатуру для  набора  какого-либо

текста... это использовать подпрограмму в ПЗУ Бейсика 48. Многие

могут мне возразить, что,  мол,  нам  не  пристало  пользоваться

подпрограммами  ПЗУ,  мы, мол, сами все можем написать. Но опять

же грустная практика показала, что  самая  удачная  подпрограмма

опроса  именно  для  набора текста - это ПЗУшная подпрограмма из

обработки прерывания IM1. Можно ее, конечно, просто  скопировать

оттуда, но не проще ли сделать RST#38?! Я использую ее следующем

образом :

 

       XOR  A

       LD   (23560),A

       LD   IY,#5C3A

       RST  #38

   

Этот участок  располагаю  внутри  моего  прерывания  IM2.  Такой

способ  конечно  не  отличается  большой  скоростью  выполнения,

однако он  очень  короткий  и  главное  -  довольно  безглючный.

Последнюю  нажатую  клавишу  затем  можно получить в любом месте

программы из ячейки по адресу 23560.  Дополнительную  информацию

про   такой   опрос   клавиатуры  можно  получить  из  различных

справочных книг по спеку. Всю эту, на первый взгляд,  ересь  для

продвинутого  кодера  я рассказываю не случайно. Дело в том, что

проблема всплывает в различных случаях, когда самые корректные с

виду  подпрограммы  опроса  клавиатуры  начинают  барахлить. Это

имеет место по различным причинам, в частности:


а) при использовании  конроллера  пц-клавы;

б) при изменении каких-то внутренних характеристик компа (обычно

Пентагона, будь он неладен) - как правило при старение железа;

в) турбирование проца.


Опять  же  отмечу,  что  это  происходит   редко,   однако   эти

прискорбные  случае  были  много  раз зафиксированы мною в нашем

регионе. Ну и, как вы уже  догадались,  единственная  программа,

которая   выдержала   проверку   по   глючности,  -  это  именно

подпрограмма опроса в ПЗУ Бейсика 48. Честно  скажу,  так  и  не

понял,  что  в  ней магического, может, потому, что она именно в

ПЗУ расположена, а может, и по  нескольким  особенностям  сразу,

только  она практически никогда не глючит. Если вы просто хотите

опросить несколько  клавиш  на  предмет  нажатости,  то  хороший

способ  -  воспользоваться  командой  IN A,(C), а не IN A,(254).

Другими словами, чтение из порта клавиатуры с  помощью  пары  BC

происходит    надежнее,   видимо,   вследствие   технологической

особенности Z80.

 

Все эти рекомендации многим могут показаться странными,  но  все

это проверено на собственном опыте. Практическое применение этих

методик вы можете найти в нашей  последней  игре  FARSPACE  (для

CC01), если залезете туда STS-ом. :)

by Fyrex/Mayhem (c) Buzz 2002

 







Прерывания from XLD                

 Что  это  такое  и  зачем нужно? Недавно

XL_DESIGN предлагалась игра, музыка в ко-

торой подтормаживала при действиях. Когда

автору  деликатно указали на данную проб-

лемму,  он  решил её просто - убрал музы-

ку...                                    

                               

 Итак, прерывание это внешний сигнал, по-

даваeмый  на  процессор  50 раз в секунду

(для  маскирoванных прерываний). Это про-

исходит в те моменты, когда видеоконтрол-

лер   начинает  рисовать  очередной  кадр

изображения(как раз 50 раз в секунду).   

 Для программиста это означает, что с на-

чалoм  каждого кадра процессору предлага-

ется  отвлечься  от  выполнения  основной

программы и перейти в программу обработки

прерывания.  Если  прерывания разрешены -

он  так  и сделает, если нет - "прeдлoжe-

ние"  проигнорируется. Прерывания делятся

на  2  типа: "маскируемые" (те, что могут

быть разрешены или запрещены) и "hemacku-

руемые"  -  никогда  не  игнорируются.  В

Speccy NMI (non masked interrupt) исполь-

зуется для обслуживания кнопки MAGIC.    

                               

 Как  происходит прерывание? По окончании

текущей команды процесор проверяет - есть

ли  сигнал прерывания и если есть, то как

реагировать  (см.  выше). Переход в прог-

рамму обработки осуществляется по принци-

пу  CALL. T.e. процессор запоминает теку-

щее  значение  PC  в стеке и переходит по

установленному для данного прерывания ад-

ресу.  Адрес зависит от режима обработки.

Режимов (interrupt mask) есть несколько: 

                               

 IM  0 - мы даже не будем o нём говорить,

так как для этого режима нужен контроллер

прерываний,   которого  нет  ни  в  одном

спектруме, а без контроллера он полностью

эквивалентен IM 1 (на стабильной машине).

 IM 1 - обрабатывается по адресу 56 (#38)

 IM 2 - режим позволяет задавать свой ад-

рес для процедуры обработки.             

                               

 NMI (немаскируемое прерывание) - oбраба-

тывается  процедурой по адресу 102 (#66).

При  нажатии  кнопки magic происходит пе-

реключение ПЗУ на ПЗУ TR-Dos'а (или в те-

heboe  ПЗУ),  где  и сидит программка его

обработки. Кстати, её тоже можно заменить

на  свою, но уже путем nepenporpammupoba-

ния ПЗУ.                                 

                               

 Теперь  поподробней. Как только вы вклю-

чите компьютер и перед вами появится зас-

тавка,  будет включен режим IM 1, который

используется, в основном, для опроса кла-

виатуры.  Этот  режим для нас особой цен-

ности не представляет, т.к. обрабатывает-

ся  только процедурами ПЗУ. Поэтому будем

говорить  про  режим IM 2. Для управления

режимами  обработки прерываний существуют

следующие команды:                       

                               

 IM 0, IM 1, IM 2                        

 DI  (Disable interrupt) - для запрещения

маскирoванных прерываний.                

 EI  (Enable  interupt)  - для разрешения

маскирoванных прерываний.                

                               

 При запрещенных прерываниях процессор на

них не будет реагировать. Heмаскирoванныe

прерывания не запрещаются.               

                               

 Для  того, чтобы отследить статус преры-

вания,  можно использовать следующую про-

цедуру:                                  

                               

   LD A,I       ; читаем статус прерывания                    

                ; во флаг четности/переполнения               

   JP PE,EI_INT ; переход, если прерывания включены           

DI_INT ...      ; прерывания выключены                        

EI_INT ...                                                    

                               

 Теперь  мы знаем как включить прерывания

и  можем обсудить способ установки своего

адреса для обработчика прерываний. (в ре-

жиме IM 2)                               

 В  процессоре  есть  специальный регистр

для  этого, это регистр I - вектор преры-

ваний. В нем указывается старший байт ад-

реса,   содержащего   адрес  обработчика.

Младший  байт читается с шины данных и на

нормальной  машине  всегда  равен 255 или

#FF.  Ho из-за того, что ненормальных ма-

шин  довольно  много (глюкoвыe пентагоны)

приходится  делать поправку на них. Глюч-

ность  их в том, что при обработке преры-

вания  у  них  с шины может читаться чёрт

знает  что.  В  связи  с  этим приходится

строить таблицу длинной 256 байт.        

                               

 Далее  мы  приведём пример "пoстрoитeля"

такой таблички. После построения он будет

занимать  512  байт  после  адреса TABLE.

Прерывания будут разрешены, то есть обра-

ботчик  к  моменту  построения должен уже

находиться в нужном месте памяти.        

                               

TABLE   EQU #FCOO ; адрес таблицы переходов размером 257 байт,

; причем  младший  байт  адреса  должен  быть равен нулю.     

; адрес #FCOO самый верхний, возможный для этой процедуры.    

                               

INT     EQU #8000 ; адрес обработчика прерывания, здесь ставь-

                  ; те нужное Вам значение.                   

IM2_MAKE                                                      

        DI                                                    

        LD HL,TABLE                                           

        LD A,H                                                

        INC A                                                 

MAKE    LD (HL),A                                             

        INC L                                                 

        JR NZ,MAKE                                            

        INC H                                                 

        LD (HL),A                                             

        DEC A                                                 

        LD I,A                                                

        IM 2                                                  

        LD HL,TABLE                                           

        INC H                                                 

        LD L,H                                                

        LD (HL),#C3                                           

        INC HL                                                

        LD DE,INT                                             

        LD (HL),E                                             

        INC HL                                                

        LD (HL),D                                             

        EI                                                    

        RET                                                   

                               

Обработчик прерываний следует делать так:

                               

INT                                                           

        DI                                                    

        PUSH AF,BC,DE,HL,IX,IY                                

        EXX                                                   

        EX AF,AF                                              

        PUSH AF,BC,DE,HL                                      

        ...                                                   

        собственные процедуры                                 

        ...                                                   

        POP HL,DE,BC,AF                                       

        EX AF,AF                                              

        EXX                                                   

        POP IY,IX,HL,DE,BC,AF                                 

        EI                                                    

        RET                                                   

                               

 Запрещать прерывания желательно, так как

есть плохо настроенные спектрумы, у кото-

рых  прерывание  может  успеть прийти еще

раз до выхода из обработчика.            

                               

 He  следует также забывать, что прерыва-

ние не резиновое, в среднем это 64-68 ты-

сяч  тактов. Если ваши процедуры не успе-

вают  завершиться за это время, то выпол-

нение  программы  замедлится  в  два раза

(вместе  с  прерываниями).  Так как самая

тормозная  машина  у  нас Скорпион и этих

скорпионов очень много, надо избегать та-

ких  ситуаций  и придерживаться барьера в

65  тысяч тактов. Вообще данная тема под-

рoбнo освещалась в ZF-3.                 

                               

 В  основном, прерывание используется для

проигрывания музыки на AY, рисования кур-

сора  и подсчета времени. Для музыки надо

только не запрещать прерываний в програм-

ме и она никогда не будет притормаживать.

                               

 Практически  все  музыкальные  редакторы

компилируют музыку вместе с плеером, его,

конечно,  можно и не присоединять, но это

вам  пока  не понадобится. Плеер работает

везде  одинаково. Сначала его надо oтынс-

таллирoвать, а потом проигрывать с часто-

той 50 Гц, то есть один раз в прерывание.

C помощью своего обработчика это делается

очень просто. В прерывании ставится вызов

плеера и все! Например так:              

                               

        CALL INSTALL                                          

        ...                                                   

INT                                                           

        DI                                                    

        PUSH AF,BC,DE,HL,IX,IY                                

        EXX                                                   

        EX AF,AF                                              

        PUSH AF,BC,DE,HL                                      

                               

        CALL PLAY                                             

                               

        POP HL,DE,BC,AF                                       

        EX AF,AF                                              

        EXX                                                   

        POP IY,IX,HL,DE,BC,AF                                 

        EI                                                    

        RET                                                   

                               

 Адреса  INSTALL и PLAY вам сообщит музы-

кальный редактор при компиляции музыки.  

                               

 Ну вот, вроде про маскированные прерыва-

ния все. Теперь пару слов o hemackupobah-

ных  прерываниях. Они имеют больший npuo-

putet, чем маскируемые, то есть процессор

считает их более важными. C помощью прог-

pammatopa  вы  сможете  сами задать адрес

обработчика  NMI (немаскируемого прерыва-

ния)  путем  замены кода в адресе 102 или

#66  в  ПЗУ  Tr-dos. Например, так сделан

теневой сервис-монитор на Scorpion'e. He-

которые  умельцы прошивают в ПЗУ STS, это

будет  получше скорпионовского сервис-мо-

hutopa.                                  

                               






from codersbucket

Interrupts on the ZX Spectrum

What are interrupts?

Interrupts, as the name suggests, are when the

 normal running of a computer program are interrupted 

so something else can happen.  On modern computers there can be 

various interrupts, but on the ZX Spectrum 

there were very few, most times you will hear of only 3 

different interrupt modes 0, 1 and 2.  While the main 

focus of this document is interrupt mode 2, we will quickly 

cover modes 0 and 1.

These three interrupts are all maskable, that is to say we 

can stop the effect of them by using the DI (Disable inter

rupts) instruction.  If we want them to start happening ag

ain we use the EI (Enable interrupts) instruction.  For

completeness I will point out that when we use the DI

instruction, the interrupts still trigger, 

that is the Z80 is still aware of them, it just ignores 

them and carries on doing what it was doing anyway. 

 Interrupt Mode 0

This interrupt mode is not really used. 

 The reason for this is that it is triggered by external hardware

. The devices must place instructions (such as RST or CALL) 

onto the data bus when it sets the interrupt request on t

he Z80.  It is this interrupt mode however that is 

running when the spectrum first powered up, luckily 

the Spectrum ROM soon setups up Interrupt Mode 1.

Interrupt Mode 1

This interrupt is very similar to IM2 (Interrupt Mode

 2) in that it is triggered by the vertical blanki

ng of the screen refresh that happens roughly 50 ti

mes a second.  The vertical blank is the time when 

the beam that draws the screen has reached the bott

om and is turned off while it moves back to the top,

 ready to draw the next frame.  Mode 1 is the 

standard interrupt used by the Spectrum while running

 basic and, unless you alter it, while your programs

 are running. 

When the vertical blank occurs the current conte

nts of the program counter (PC) are pushed onto 

the stack (SP).  The address $0038 is then loade

d into PC and the Spectrum will start running 

the program stored at this location.  As you 

may notice by the address, this is in the 

ROM and as such we have no way of altering what this does.  

The ROM routine mainly scans the keyboard and stores details 

of what is being pressed.

Interrupt Mode 2

As mentioned IM2 is very similar to IM1 above e

xcept it is known as a vectored interrupt.  Like

 IM1 it is triggered roughly 50 times a second on

 the vertical blank, it pushes the current PC onto

 the stack but then rather than jumping to a specific

 address it uses a vector table to find out what 

value to load into PC.  There is a lot of confusion 

over the exact operation of this vector table, and 

as different emulators deal with it in different 

ways, it is best to go with the method that works for all. 

The vector table is simple a list of memory addres

ses, 128 of them in total.  The table sits on a 25

6 byte boundary and the high byte of its address i

s loaded into the special I register on the Z80.  

The confusion seems to occur with what fills in the

 bottom 8 bits.  The value comes from the BUS and 

general feeling is (and as such the safest rule to

 follow) that any value can occur.  Some documenta

tion such as ZAKS (Programming the Z80 book) will 

say that only the higher 7 bits will be used from 

the BUS, the least significant bit will always be 

0.  While to me this seems the most logical given 

the vector table, as mentioned not all emulators f

ollow this rule and as such I would strongly advise

 against assuming this bottom bit is 0.  Why does 

it make a difference you may ask? To answer that 

lets look at what happens when the interrupt is triggered.    

When the interrupt is triggered, and they are enabled,

 the contents of the PC are pushed onto the stack. 

The contents of the I register are loaded into t

he upper 8 bits of the address bus, the BUS suppl

ies the lower 8 bits, as we are not using any devi

ces these bits can be any value.  .  Given this

 address ((I * 256) + BUS bits) a two byte address

 is read and loaded into the PC. The Z80 will then

 run the code stored at this address as per normal. 

First lets assume that the I register is set to 

$40 and that the bottom bit is always 0.  When 

the interrupt is triggered the memory address 

will we calculated as $40xx where xx is any eve

n 8 but value (0, 2, 4… 254).  We can simply st

ore the address of our interrupt routine at all

 128 locations $4000, $4002, $4004 …… $40fe.  

This way when the interrupt occurs the same r

outine address will be loaded into the program 

counter no matter what the BUS value is.  

The code to set this up would be something like


      ORG   $8000

; Setup the 128 entry vector table

di

      ld     hl, VectorTable

      ld     de, IM2Routine

      ld     b, 128

      ; Setup the I register (the high byte of the table)

      ld     a, h

      ld     i, a

      ; Loop to set all 128 entries in the table

_Setup:

      ld     (hl), e

      inc   hl

      ld     (hl), d

      inc   hl

      djnz   _Setup

      ; Setup IM2 mode

      im     2

      ei

      ret

; Basically nothing

IM2Routine:  

      ei

      reti

; Make sure this is on a 256 byte boundary

      ORG   $F000

VectorTable:

      defs   256

Hopefully the code shows that the vector table stores

 the 128 entries pointing to the routine IM2Routine.  

When the interrupt is triggered the byte address at 

(I * 256) + Bus will be read along with the following byte

 to form the address.  The routine at this address will

 be called, in our example no matter what the Bus is it 

will always call IM2Routine.  But what happens if we 

don’t assume bit 0 will always be 0?

In our example above IM2Routine will have the 

address $8016, so the first eight bytes of our vector 

table will be

defb   $16, $80, $16, $80, $16, $80, $16, $80

The rest of the table will follow the same pattern.

  When bit 0 was always 0 the low byte would always

 be pulled out as $16 and the following high byte o

f the address would be $80 (assuming Bus was 0 the 

low byte would be pulled out from the start of the 

table and the high byte would be the next byte), th

e next option would be Bus was 2 which would give 

the same result just reading from 2 bytes into the

 table.  If however bit 0 was not always 0 we cou

ld end up reading starting from byte 1 in the tab

le giving our low byte of $80 and our high byte of $16.  

This gives us the address $1680 which certainly isn’t 

where we want the Z80 to jump to.

Given the reasons behind how the vectored interrupt 

is designed I would speculate that the hardware doe

s indeed always reset bit 0 to 0 however as mention

ed not all emulators follow this rule (The hardware 

ive tested this on does mind you).  To be safe it

s best to work on the theory it can do a read from 

any memory address.  So, how do you deal with that?

  Simply make sure all the bytes in the table are 

the same value.

One other thing to note with the issue of readin

g from any byte is what happens if the Bus value

 is 255 when the interrupt triggers.  This will 

be used as the low end byte of the address on the

 vector table, but as we need to read two bytes it

 will read the last entry in the 256 byte table 

as well as the byte following it.  To allow for this we

 must use a 257 byte table when allowing for any Bus value.

  So with that in mind lets take a look at the code 

to setup the table.

      ORG   $8000

; Setup the 128 entry vector table

di

      ld     hl, IM2Table

      ld     de, IM2Table+1

      ld     bc, 256

    ; Setup the I register (the high byte of the table)

      ld     a, h

      ld     i, a

      ; Set the first entries in the table to $FC

      ld     a, $FC

      ld     (hl), a

      ; Copy to all the remaining 256 bytes

      ldir

      ; Setup IM2 mode

      im     2

      ei

      ret

; This routine now needs to be at a specific address 

(remember we only have from $FCFC to $FE00 else we 

will overwrite our table)

      ORG   $FCFC

; Basically nothing

IM2Routine:  

      ei

      reti

; Make sure this is on a 256 byte boundary

      ORG   $FE00

IM2Table:

      defs   257

Why do we need an interrupt routine?

Having looked at how we setup an interrupt routine, 

you may find yourself asking why you need one. 

 Well there are a few good reasons to set one up,

 firstly why let the ROM waste good cpu cycles doing 

something that you may not need?  The main reason 

however often comes down to timing, especially with 

things like music.

A music player will usually require that you call an

 update function every frame, which is 50 times a se

cond.  If you don’t do this then the music will star

t to sound very strange indeed as frequencies etc wil

l all go wrong.  While it might seem easy to do this 

if you know your program will always run in a frame, 

when you start to write more complex programs and gam

es this might not always be the case.  In a game you 

may have an unknown number of enemies on the screen 

firing an unknown number of bullets.  While your code

 might happily run in a frame if there are 5 enemies, 

what happens when the 6th one appears?

To solve this we can put the music update routine into

 the interrupt, then no matter how long the game takes

 to update the music will continue to be updated at a

 constant 50 frames a second.  To do this will simply 

add a call to the music player update in the IM2Routine. 

; Update a music player

IM2Routine:  

      call   Music_Update

      ei

      reti

Seems easy enough right? Well yes and no, the chance

s are this would crash your computer horribly.  The r

eason is that the interrupt can occur at absolutely a

ny time and it doesn’t care what the state of your re

gisters are.  When we just had EI/RETI it was fine, u

nfortunately the music update function will most like

ly alter registers, and even if it handles storing and

 restoring them all itself, if it very good practice 

to store and restore them yourself to be sure. 

; Update a music player with register storing / restore

IM2Routine:  

      push   af

      push   hl

      push   bc

      push   de

      push   ix

      push   iy

      exx

      ex     af, af’

      push   af

      push   hl

      push   bc

      push   de

      call   Music_Update

      pop   de

      pop   bc

      pop   hl

      pop   af

      ex     af, af’

      exx

      pop   iy

      pop   ix

      pop   de

      pop   bc

      pop   hl

      pop   af

      ei

      reti

Just to clear up a few things…

The Z80 provides 2 other interrupts that are not 

maskable, that is you cannot disable them with the

 DI instruction, they are the NMI and BUSRQ.  To m

y knowledge the BUSRQ was never used as this was d

esigned to allow DMA systems to work along side the

 Z80, something the spectrum never had.  

The NMI (Non-Maskable Interrupt) was used on the

 Spectrum but was only available via additional hardware. 

Certain hardware devices had a button that would trigger

 a NMI which would cause the current program to stop 

and the routine at address $0066 to be executed.  

As this address is fixed and in the ROM it is of 

very little use for software programmers.

RET / RETI

You may have noticed that the interrupt routine 

ends with RETI rather than the standard RET, thi

s is for completeness and not specifically requi

red, you can end with RET if you prefer.  The ma

in reason RETI was added to the instruction set i

s to allow hardware to detect when the end of the in

terrupt routine and to pass control onto the next in

terrupt in a dasiy chained system.  The hardware coul

d specifically check the opcode for RETI.  If it 

checked RET it could get confused when coming to the end 

of a function rather than the entire interrupt routine.

  To my knowledge no devices for the spectrum 

use this feature.

EI but not DI?

There is another oddity in the interrupt routine

, we have an EI at the end but we never do a DI

.  This is due to the fact the Z80 will do an i

nternal DI when it starts processing the interrupts.

  The reason for this is to stop multiple interrupts

 triggering on top of each other and overrunning the

 system stack.  You can do an EI before the end of 

your interrupt routine if you wish but general practice is

 to wait until you have finished then enable them, 

that way there is no chance of you starting a new 

interrupt routine before the old one has finished.







;from breakintoprogram.co.uk

In order to make your game compatible with the 128K Spectrum, 

you will need to create your vector table.

I usually arrange my memory map as follows:

SP @ 0x000 – so it grows down from the top of RAM

Interrupt vector table @ 0xFE00 (257 bytes) with the value 0xFD

A 3 byte jump instruction @ 0xFDFD to the interrupt routine

This is quite economical on memory, and will ensure that 

your 48K game is compatible with the 128K Spectrum.

If you are planning on using memory paging on the 128K Spectrum, 

I’d suggest moving the interrupt routine elsewhere. 

The above code will work,

but remember to set IM2_JP to an address where the high and low bytes are the same.

Stack_Top EQU #0000     ; Stack at top of RAM

IM2_Table EQU #FE00       ; 256 byte page (+ 1 byte) for IM2

IM2_JP EQU #FDFD     ; 3 bytes for JP routine under IM2 table

Initialise_Interrup DI

LD DE, IM2_Table; The IM2 vector table (on page boundary)

LD HL, IM2_JP  ; Pointer for 3-byte interrupt handler

LD A, D ; Interrupt table page high address

LD I, A ; Set the interrupt register to that page

LD A, L ; Fill page with values

lll LD (DE), A 

INC E

JR NZ, lll

INC D ; In case data bus bit 0 is not 0, we

LD (DE), A ; put an extra byte in here

LD (HL), #C3 ; Write out the interrupt handler, a JP instruction

INC L

LD (HL), low Interrupt; Store the address of the interrupt routine in

INC L

LD (HL), high Interrupt

IM 2; Set the interrupt mode

EI ; Enable interrupts

RET

Interrupt DI ; Disable interrupts 

PUSH AF ; Save all the registers on the stack

PUSH BC ; This is probably not necessary unless

PUSH DE ; we're looking at returning cleanly

PUSH HL ; back to BASIC at some point

PUSH IX

push iy

EXX

EX AF,AF'

PUSH AF

PUSH BC

PUSH DE

PUSH HL

;;;;PUSH IY

; Your code here...call proc1;call proc2 etc

;;;;POP IY; Restore all the registers

POP HL

POP DE

POP BC

POP AF

EXX

EX AF,AF'

pop iy

POP IX

POP HL

POP DE

POP BC

POP AF

EI ; Enable interrupts

RET ; And return









Т.е. тебе нужно ждать нужное кол-во тактов? 

Так бы стразу и сказал 

ld bc,нужное число тактов

call DELAY;ждёт нужно число тактов из bc>141такта

; Z80 delay routine; by Jan Bobrowski, license GPL, LGPL

DELAY:  

; wait bc T (including call; bc>=141)

; destroys: af, bc, hl

ld hl, -141

add hl, bc

ld bc, -23

_loop add hl, bc

jr c, _loop

ld a, l

add a, 15

jr nc, _g0

cp 8

jr c, _g1

or 0

_g0 inc hl

_g1 rra

jr c, _b0

nop

_b0 rra

jr nc, _b1

or 0

_b1 rra

ret nc

        ret






Так же повотрю программу для  определе-

ния количества тактов  между  прерываниями

компьютера.

В регистре DE кол-во тактов  деленное

на десять (например  7166  это  71660

тактов).

ORG >#7FFF ;!!!!

DI

LD HL,#FE00 ; Организация режима

LD A,H      ; IM 2

LD I,A

DEC A

LD (HL),A

INC L

JR NZ,$-2

INC H

LD (HL),A

LD A,#C9     ; Временно блокируем

LD (#FDFD),A ; программу возврата

LD HL,RETURN ; Заносим адрес возв-

LD (#FDFE),HL; рата из подсчета

IM 2

LD DE,0    ; Обнуляем счетчик

LD HL,CYC

LD A,#C3

EI

HALT  ; холостой прогон

EI

HALT  ; холостой прогон

EI

LD (#FDFD),A ; Разблок. возврат

CYC     INC DE ; считаем такты

JP (HL)

RETURN  DI

POP AF ; приводим стек в исходное

INC DE ; состояние

INC DE







;(C) J/C.I.C.

   В этой статье я кратко изложу принцип  программного  подсчета

количества тактов в строке (за  сколько тактов  видео-контроллер

перешлет 1 строку изображения  на монитор).  Для чего это  надо:

для создания крутых эффектов на бордюре, multicolor'а и т.п. Эти

эффекты будут правильно работать на всех компах кроме динозавров

с медленной памятью и машине, где количество строк  по вертикали

не 320 или 312. Програмный изврат, о котором я Вам  сейчас пове-

даю, будет автоматически настраиваться под любой  попавшийся ему

в руки комп, но надо будет только ему указать количесво строк по

вертикали и все !

   А теперь ближе к делу. Сначала немного  теории. Как, Вы дума-

ете, задается количество тактов между  прерываниями ? Может где-

то на плате запаян счетчик который и отсчитывает нужное количес-

тво ? Нет ! Все совсем проще. Как  известно, у нормального компа

312 или 320  вертикальных строк  изображения (вместе с экраном и

бордюром). Так вот, когда ULA (video-контроллер) эти  строки пе-

редаст монитору, приходит прерывание, а затем все опять по  цик-

лу. Но у компов разное  количество тактов между  прерываниями, а

количество строк совпадает. Значит, у них за разное время переб-

расывается 1 строка. Известно  также, что ULA за 1  такт при 3.5

мгц перебрасывает 2 точки на монитор (и это  константа). Значит,

на разных компьютерах разное количество точек по горизонтали !!!

   Теперь можно вывести формулу: количество строк  *  количество

тактов в строке = количество тактов между прерываниями  -  и это

со 100%-й точностью !!! Допустим,  что количество  тактов  между

прерываниями приблизительно можно  сосчитать.  Количество  строк

укажет user (или coder) при инсталяции,  и количество  тактов  в

строке неизвестное, которое можно найти опять же по формуле (это

математика): количество тактов между прерываниями / на количест-

во строк = неизвестное (если будет остаток при делении ,  то ре-

зультат нужно будет от-INC'рементить).

   А теперь программа:


DI

LD      A,#18

LD      (#FFFF),A

LD      A,#C3

LD      (65524),A

LD      HL,INT1

LD      (65525),HL     ;адрес обработки  прерывания

LD      HL,#BE00

LD      DE,#BE01

LD      (HL),255

LD      A,H

LD      I,A

LD      BC,#0100       ;генерируем таблицу векторов

LDIR   ;прерывания

IM      2       ;установка 2 режима прерывания

LD      DE,0

LD      HL,INT2

EI

HALT

......

INT1    LD      (65525),HL  ;новый адрес обработки прерывания

LD      HL,INT3

EI

HALT

......

INT2    LD      (65525),HL     ;опять кладется новый адрес

EI

INC     DE     ;цикл подсчета тактов

JP      $-1

;

INT3    POP     HL

POP     HL     ;для того чтобы вернуться по RET

EX      DE,HL   ;к первому HALT'у

ADD     HL,HL   ;HL * 8

ADD     HL,HL

ADD     HL,HL

LD      A,N     ;N=0 если 312 строк, иначе 320

OR      A

LD      BC,156 ;а вот догадайтесь сами, почему

JP      Z,INT4 ;156 вместо 312 и 160 вместо 320

LD      BC,160

XOR     A

INT4    OR      A

SBC     HL,DE   ;деление как по формуле ...

INC     A

JP      NC,INT4 ;теперь в A будет количество

      ;тактов в строке.


   Поясню, что 3 раза я записывал разный адрес обработки  преры-

вания для того,  чтобы на некоторых  глючных компах  (на моем  к

примеру) все работало О.К., а если вместо 3 раз будет 2 раза, то

на выходе в A может запросто оказаться 1.








TAPE

Header:

0 - тип блока (0=programm, 1= number array, 2=character array, 3=bytes)

1-10 имя блока (10 байт)

11-12 длина блока

13-14 bytes: адрес, program: номер строки старта (line xx), array:

 14=имя переменной, 13 не учитывается

15 используется только Program: длина программ бейсика (bytes и arrays не используется)




Скорость

Из-за того, что разные биты на ленте занимают разное время, процедура 

ленты Spectrum не имеет четко определенной скорости. Однако

мы можем рассчитать некоторые пределы и типичные значения; как 

отмечалось выше, бит «0» занимает 2 × 855 = 1710 T-состояний, а бит

«1» занимает 2 × 1710 = 3420 T-состояний. Если предположить, 

что Спектрум 48K работает на частоте 3,50 МГц (машины 128K работают 

на частоте 3,54 МГц или примерно на 1% быстрее; 

здесь эта разница практически не имеет значения), это означает, что:

Поток чистых нулевых «0» битов загружается со скоростью 3500000 ÷ 1710 ≈ 2046 бод.

Поток чистых битов «1» загружается со скоростью 3500000 ÷ 3420 ≈ 1023 бод.

Смешанный поток с равным количеством битов «0» и «1» загружается со скоростью 

2 × 3500000 ÷ (3420 + 1710) ≈ 1364 бод.

В реальных данных Spectrum, как правило, было больше 

битов «0», чем битов «1», поэтому типичная скорость передачи данных 

на самом деле была немного выше, чем 1364 бод.







;load bytes w/o header ;save

LD IX,addr

LD DE,len

LD А,#ff ;load bytes

SCF ;вроде не нужен при записи 

CALL #556 ;rom proc LD-BYTE / #4C2 = SA-BYTES





Load=Merge 

ld bc,#0022

rst #30

push  de

pop ix

ld (iy+#3a),1

ld (ix+1),#ff

call #071d

ld hl,(#5c42)

ld (#5c45),hl

rst 8

db #ff




.TAP

формат для хранения образа кассеты. Позволяет хранить данные, 

сохранённые стандартными процедурами Sinclair BASIC для работы 

с кассетой на ZX Spectrum. Для хранения данных в нестандартных 

форматах предназначен более сложный формат TZX.


Файл в формате TAP может содержать один или несколько блоков. 

Каждый блок состоит из двухбайтового заголовка с длиной 

последущих данных и непосредственно данных, включая флаги 

и контрольную сумму. Отдельные TAP-файлы могут легко "склеиваться" 

вместе, например, командой 

COPY /B file1.tap + file2.tap all.tap в MS-DOS.


==========================================================================================================

Offset:  Field type:    Length:   Description:  Additional information:


   0    WORD(LSB,MSB)      2     [data length]  length of the following datablock in bytes (0..65535)

   2    Tape data    [data length]              data as it is stored on tape, may be a header or

                                                any data from ZX-Spectrum

----------------------------------------------------------------------------------------------------------


Valid data blocks - headers (always 19 bytes long):


==========================================================================================================

case #1: program header or program autostart header - for storing BASIC programs


Offset:  Field type:    Length:   Description:  Additional information:


   0        BYTE           1       flag byte    always 0. Byte indicating a standard ROM loading header

   1        BYTE           1       data type    always 0: Byte indicating a program header

   2   ARRAY 10 OF CHAR   10       file name    loading name of the program. filled with spaces (CHR$(32))

  12        WORD           2     [data length]  length of the following data (after the header)

                                                = length of BASIC program + variables

  14        WORD           2    autostart line  LINE parameter of SAVE command. Value 32768 means

                                                "no auto-loading"; 0..9999 are valid line numbers.

  16        WORD           2  [program length] length of BASIC program; remaining bytes

                                                  ([data length] - [program length]) = offset of variables

  18        BYTE           1     checksum byte  simply all bytes (including flag byte) XORed


==========================================================================================================

case #4: byte header or SCREEN$ header - for storing machine code or screens


Offset:  Field type:    Length:   Description:  Additional information:

   0        BYTE           1       flag byte    always 0. Byte indicating a standard ROM loading header

   1        BYTE           1       data type    always 3: Byte indicating a byte header

   2   ARRAY 10 OF CHAR   10       file name    loading name of the program. filled with spaces (CHR$(32))

  12        WORD           2     [data length]  length of the following data (after the header)

                                                in case of a SCREEN$ header = 6912

  14        WORD           2     start address  in case of a SCREEN$ header = 16384

  16        WORD           2        unused      = 32768

  17        BYTE           1     checksum byte  simply all bytes (including flag byte) XORed

----------------------------------------------------------------------------------------------------------


Valid data blocks - data blocks:

==========================================================================================================

case #5: standard data blocks or custom data blocks - (2+[data length]) bytes


Offset:  Field type:    Length:   Description:  Additional information:

   0        BYTE           1       flag byte    always 255 indicating a standard ROM loading data block or

                                                any other value to build a custom data block

   1        BYTE    [data length]  data block   the essential data (may be empty)

1+[data length] BYTE       1     checksum byte  simply all bytes (including flag byte) XORed

----------------------------------------------------------------------------------------------------------






Разное...

SCREEN FADEIN & FADEOUT

ClS LD HL,16384 ; Start address of screen bitmap

LD DE,16385 ; Address + 1

LD BC,6144 ; Length of bitmap memory to clear

LD (HL),0 ; Set the first byte to 0

LDIR ; Copy this byte to the second, and so on

LD BC,767 ; Length of attribute memory, less one to clear

LD (HL),A ; Set the first byte to A

LDIR ; Copy this byte to the second, and so on

RET





;

FADEIN LD HL,DEPK_SCR

        LD DE,#4000

        LD BC,#1800

        LDIR 

        LD B,8

FAIN3 ;;;HALT

        LD HL,#5800

        LD DE,DEPK_SCR+#1800

FAIN2   LD A,(DE)

        AND #3F

        JR Z,FAIN1

        LD A,(DE)

        AND #C0

        OR (HL)

        LD (HL),A

        LD A,(DE)

        XOR (HL)

        AND #07

        JR Z,FAIN4

        INC (HL)

FAIN4   LD A,(DE)

        XOR (HL)

        AND #38

        JR Z,FAIN1

        LD A,(HL)

        ADD A,#08

        LD (HL),A

FAIN1   INC DE

        INC HL

        LD A,H

        CP #5B

        JR C,FAIN2

        DEC A

        DJNZ FAIN3

        RET 


;FADEOUT

FADEOUT LD B,#08

FADEL                   ;;;HALT

        LD HL,#5800

        LD A,(HL)

        AND A

        JR Z,FADO1

FADO3   LD A,(HL)

        AND #07

        JR Z,FADO2

        DEC (HL)

FADO2   LD A,(HL)

        AND #38

        JR Z,FADO1

        LD A,(HL)

        SUB #08

        LD (HL),A

FADO1   INC HL

        LD A,H

        CP #5B

        JR C,FADO3

        DJNZ FADEL






; Fill a box of the screen with a solid colour

;  A: The colour

; HL: Address in the attribute map

;  C: Width

;  B: Height

;

Fill_Attr: LD DE,32

1xxx: PUSH HL

PUSH BC

2xxx: LD (HL), A

INC L

DEC C

JR NZ, 2xxx

POP BC

POP HL

ADD HL,DE

DJNZ 1xxx

RET






;fullscreen (paper+border) flasher

FLASHSCREEN ld   ix,flashdata

FLASHCIKL halt

ld   a,(ix+00)

inc  ix

cp   FF

ret  z

out  (FE),a

ld   c,a

rlca

rlca

rlca

or   c

ld   hl,5800

ld   de,5801

ld   bc,02FF

ld   (hl),a

ldir

jr   FLASHCIKL

flashdata db 01,02,03,04,05,06,07,06,05,04,03,02,01,00,FF





;random fade pixels

PIXFA LD   B,8

      LD   DE,0

PF2   LD   HL,#4000

      PUSH DE

PF3   LD   A,(DE)

      AND  (HL);if XOR - white noise

      LD   (HL),A

      INC  HL

      INC  DE

      LD   A,H

      CP   #58

      JR   NZ,PF3

      POP  DE

      LD   HL,100

      ADD  HL,DE

      EX   DE,HL

      DJNZ PF2

      RET





;random fade pixels & attrs from Lode Runner

lrfade ld b,4

ld de,0

lrfad2 push bc

ld bc,#1b00

ld hl,#4000

lrfad1 ld a,(de)

and (hl)

ld (hl),a

inc hl

dec bc

inc de

ld a,c

or b

jr nz,lrfad1

pop bc

halt

djnz lrfad2

ret



;double shift left-right vertical lines

        LD      B,8

LOOP2   LD      HL,16384

        PUSH    BC

  ;     HALT

        LD      BC,#0C00

LOOP1   SLA     (HL)

        INC     HL

        SRL     (HL)

        INC     HL

        DEC     BC

        LD      A,B

        OR      C

        JR      NZ,LOOP1

        POP     BC

        DJNZ    LOOP2

        RET 




;up to down line R clear scree

        ORG     40000

        LD      HL,16384

        LD      B,192

LOOP    PUSH    BC

        PUSH    HL

        PUSH    HL

        CALL    LINE

        POP     HL

        CALL    CLS

        POP     HL

        CALL    ADDR

        POP     BC

        DJNZ    LOOP

        RET 

LINE    LD      B,32

        LD      A,R

        AND     63

        LD      D,A

L1      LD      A,(DE)

        LD      (HL),A

        INC     HL

        INC     DE

        DJNZ    L1

        LD      BC,#BBB ;wait

PAUSE   DEC     BC

        LD      A,B

        OR      C

        JR      NZ,PAUSE

        RET 

CLS     PUSH    HL

        POP     DE

        INC     DE

        LD      (HL),0

        LD      BC,31

        LDIR 

        RET 

ADDR    INC     H

        LD      A,H

        AND     7

        RET     NZ

        LD      A,L

        ADD     A,32

        LD      L,A

        RET     C

        LD      A,H

        SUB     8

        LD      H,A

        RET 





;затухание линия за линией

begin ei

ld hl, #5720

ld c, #1E

loc_606B push hl

ld b, 0

loc_606E ld a, h

cp #58

jr c, loc_6088

ld a, (hl)

and 7

jr z, loc_6079

dec a

loc_6079 ld d, a

ld a, (hl)

and #38

jr z, loc_6081

sub 8

loc_6081 or d

ld d, a

ld a, (hl)

and #C0

or d

ld (hl), a

loc_6088 inc hl

ld a, h

cp #5B

jr z, loc_6090

djnz loc_606E

loc_6090 halt

ld de, #20

pop hl

add hl, de

dec c

jr nz, loc_606B

  ret






diagfad ei

halt

pv1 ld e,0 ;X

ld d,0 ;Y

plp0 ld l,d,h,0

add hl,hl

add hl,hl

add hl,hl

add hl,hl

add hl,hl

ld c,e

ld b,#58

add hl,bc

ld (hl),0

inc d

ld a,d

cp 24

jr nc,pv2

dec e

jp p,plp0

pv2 ld e,31 ;X

ld d,23

plp1 ld l,d,h,0

add hl,hl

add hl,hl

add hl,hl

add hl,hl

add hl,hl

ld c,e

ld b,#58

add hl,bc

ld (hl),0

dec d

jp m,exlp1

inc e

bit 5,e

jp z,plp1

exlp1 ld a,(pv2+1)

dec a

ld (pv2+1),a

ld a,(pv1+1)

inc a

ld (pv1+1),a

cp 32-4

jp nz,diagfad

ret





Some Procedures from game Dominion (?) by Players

 ****************************************

 *      FADE SCREEN DOWN TO ZERO        *

 ****************************************

 

 FADEOUT LD B,8

 FADE2 PUSH BC

EI

HALT


  LD HL,22528+256

  LD BC,512

 FADE3 LD A,(HL)

AND A

  JP Z,NOTHING

RES 7,A

  RES 6,A

  RES 5,A

  RES 4,A

  RES 3,A

  DEC A

  LD (HL),A

 NOTHING INC HL

  DEC BC

LD A,B

OR C

JP NZ,FADE3

 

  LD B,3

CALL PAUSE


  POP BC

DJNZ FADE2


  LD HL,18432

  LD DE,18433

  LD (HL),L

  LD BC,4095

LDIR

RET

 

 ****************************************

 *    FADE WHOLE SCREEN DOWN TO ZERO *

****************************************

 

 TOTALFADE LD B,8

 FADE25 PUSH BC

  EI

HALT


  LD HL,22528

  LD BC,768

 FADE35 LD A,(HL)

  AND 7

  LD (HL),A

  AND A

  JP Z,NOTHING5

 

  DEC A

  LD (HL),A

 NOTHING5 INC HL

DEC BC

LD A,B

OR C

JP NZ,FADE35


  LD B,3

CALL PAUSE


  POP BC

DJNZ FADE25

 

  LD HL,16384

  LD DE,16385

  LD (HL),L

  LD BC,6911

LDIR


  JP CLEARMASK


 ****************************************

 *        TOTAL FADE IN SCREEN          *

 ****************************************

 

 TOTALFADEU LD B,8

FADE257 PUSH BC

EI

HALT


  LD HL,22528

  LD BC,768

 FADE357 LD A,(HL)

CP 7

JP Z,NOTHING57


  INC A

  LD (HL),A

 NOTHING57 INC HL

  DEC BC

LD A,B

OR C

JP NZ,FADE357

 

  LD B,3

CALL PAUSE


  POP BC

DJNZ FADE257

RET

 

 ****************************************

 *        FADE SCREEN UP TO ZERO        *

 ****************************************

 

 FADEIN LD B,8

FADE23 PUSH BC

  EI

HALT


  LD HL,22528+256

  LD BC,512

 FADE33 LD A,(HL)

  CP 7

JP Z,NOTHING3

 

  INC A

  LD (HL),A

 NOTHING3 INC HL

DEC BC

LD A,B

OR C

JP NZ,FADE33


  LD B,3

CALL PAUSE


  POP BC

DJNZ FADE


SCROLLERS

ADDRP1  EQU #4067;адрес верхней линии для сдвига вверх

ADDRP2  EQU #4067+32;адрес на знакоместо ниже

ADDRA1  EQU #5867;то же самое

ADDRA2  EQU #5867+32;для атрибутов

ADDRP3  EQU #57C7-32;адрес линии для сдвига вниз

ADDRP4  EQU #57C7;адрес на знакоместо ниже

ADDRA3  EQU #5AC7-32;то же самое

ADDRA4  EQU #5AC7;для атрибутов

SHIR    EQU 8    ;ширина сдвигаемого окна

DLINA   EQU 19*8 ;длина --//-- NB! для сдвига вниз это значение должно быть меньше на еденицу


;Сдвиг произвольного окна (графика+атрибуты) вверх(C) Wizard/DTr

SCR_UP LD HL,ADDRP2

        LD DE,ADDRP1

        LD B,DLINA

SCRLU_P PUSH BC

        PUSH DE

        PUSH HL

        LD BC,SHIR

        LDIR 

        POP HL

        CALL DOWN_HL

        POP DE

        CALL DOWN_DE

        POP BC

        DJNZ SCRLU_P

        LD HL,ADDRA2

        LD DE,ADDRA1

        LD B,DLINA/8

SCRLU_A PUSH BC

        PUSH DE

        PUSH HL

        LD BC,SHIR

        LDIR 

        POP HL

        POP DE

        LD B,32

scrlu   INC HL

        INC DE

        DJNZ scrlu

        POP BC

        DJNZ SCRLU_A

        RET 


;Сдвиг произвольного окна (графика+атрибуты) вниз (C) Wizard/DTr

SCR_DN LD HL,ADDRP3

        LD DE,ADDRP4

        LD B,DLINA

SCRLD_P PUSH BC

        PUSH DE

        PUSH HL

        LD BC,SHIR

        LDIR 

        POP HL

        CALL UP_HL

        POP DE

        CALL UP_DE

        POP BC

        DJNZ SCRLD_P

        LD HL,ADDRA3

        LD DE,ADDRA4

        LD B,DLINA/8

SCRLD_A PUSH BC

        PUSH DE

        PUSH HL

        LD BC,SHIR

        LDIR 

        POP HL

        POP DE

        LD B,32

scrld   DEC HL

        DEC DE

        DJNZ scrld

        POP BC

        DJNZ SCRLD_A

        RET 

;

DOWN_HL INC H

        LD A,H

        AND 7

        RET NZ

        LD A,L

        ADD A,32

        LD L,A

        RET C

        LD A,H

        SUB 8

        LD H,A

        RET 

DOWN_DE INC D

        LD A,D

        AND 7

        RET NZ

        LD A,E

        ADD A,32

        LD E,A

        RET C

        LD A,D

        SUB 8

        LD D,A

        RET 

UP_HL   DEC H

        LD A,H

        CPL 

        AND 7

        RET NZ

        LD A,L

        SUB 32

        LD L,A

        RET C

        LD A,H

        ADD A,8

        LD H,A

        RET 

UP_DE   DEC D

        LD A,D

        CPL 

        AND 7

        RET NZ

        LD A,E

        SUB 32

        LD E,A

        RET C

        LD A,D

        ADD A,8

        LD D,A

        RET 

;LEN    EQU $-#6000

;       DISPLAY "Длина модуля ",LEN," мегабайт"




;--------------------------------------;

;        FULL SCREEN SCROLL UP     1   ;

;       coded by Kolotov Sergey        ;

; (c) SerzhSoft, Shadrinsk, may, 1998  ;

;--------------------------------------;

_NULL   EQU     0

;--------------------------------------;

_DATA   EQU     #6000

SCR_TBL EQU     _DATA           ;,#0300

DATAEND EQU     SCR_TBL+#0300

;--------------------------------------;

        ORG     #8000

;--------------------------------------;

MAINPRG

        EI

;

        CALL    MK_STBL

;        CALL    MKSRLUP

;

        LD      HL,#0000

        LD      DE,#4000

        LD      BC,#1800

        LDIR

;

        LD      B,#C0

LP_MAIN PUSH    BC

        HALT

        DI

        CALL    SRL_UP

        EI

        POP     BC

        DJNZ    LP_MAIN

;

        RET

;--------------------------------------;

MK_STBL

        LD      HL,SCR_TBL

        LD      DE,#4000

        LD      B,#C0

LP_MSTB LD      (HL),E

        INC     HL

        LD      (HL),D

        INC     HL

;

        INC     D

        LD      A,D

        AND     #07

        JR      NZ,$+12

        LD      A,E

        ADD     A,#20

        LD      E,A

        JR      C,$+6

        LD      A,D

        SUB     #08

        LD      D,A

;

        LD      (HL),E

        INC     HL

        LD      (HL),D

        INC     HL

        DJNZ    LP_MSTB

;        RET

;--------------------------------------;

MKSRLUP

        LD      HL,SRL_UP

        LD      (HL),#ED  ;ld (...),sp

        INC     HL

        LD      (HL),#73

        INC     HL

        PUSH    HL

        INC     HL

        INC     HL

        LD      (HL),#01  ;ld bc,...

        INC     HL

        LD      (HL),#E0

        INC     HL

        LD      (HL),#17  ;ld bc,#17E0

        INC     HL

        LD      (HL),#31  ;ld sp,...

        INC     HL

        LD      DE,SCR_TBL

        LD      (HL),E

        INC     HL

        LD      (HL),D    ;ld sp,SCR_TBL

        INC     HL

        PUSH    HL        ;lp_srup

        LD      (HL),#D1  ;pop de

        INC     HL

        LD      (HL),#E1  ;pop hl

        INC     HL

        LD      B,#20

LP_MSU1 LD      (HL),#ED  ;ldi

        INC     HL

        LD      (HL),#A0

        INC     HL

        DJNZ    LP_MSU1

        LD      (HL),#EA  ;jp pe,...

        INC     HL

        POP     DE

        LD      (HL),E

        INC     HL

        LD      (HL),D    ;jp pe,lp_srup

        INC     HL

        LD      (HL),#31  ;ld sp,...

        INC     HL

        LD      (HL),B    ;#00

        INC     HL

        LD      (HL),#58  ;ld sp,#5800

        INC     HL

        LD      B,#10

LP_MSU2 LD      (HL),#C5  ;push bc

        INC     HL

        DJNZ    LP_MSU2

        LD      (HL),#31  ;ld sp,_NULL

        INC     HL

        EX      DE,HL

        POP     HL        ;ld (...),sp

        LD      (HL),E    ;    ^^^

        INC     HL

        LD      (HL),D

        EX      DE,HL

        INC     HL

        INC     HL

        LD      (HL),#C9  ;ret

        RET

;--------------------------------------;

_CODE   EQU     $

SRL_UP  EQU     _CODE           ;,#0066

CODEEND EQU     SRL_UP+#0066

;--------------------------------------;





;--------------------------------------;

;        FULL SCREEN SCROLL UP    2    ;

;       coded by Kolotov Sergey        ;

; (c) SerzhSoft, Shadrinsk, may, 1998  ;

;--------------------------------------;

_NULL   EQU     0

LNS_NUM EQU     #40

;--------------------------------------;

_DATA   EQU     #6000

SCR_TBL EQU     _DATA           ;,#0300

DATAEND EQU     SCR_TBL+#0300

;--------------------------------------;

        ORG     #8000

;--------------------------------------;

MAINPRG

        EI

;

        CALL    MK_STBL

;        CALL    MKSRLUP

;

        LD      HL,#0000

        LD      DE,#4000

        LD      BC,#1800

        LDIR

;

        LD      B,#C0

LP_MAIN PUSH    BC

        HALT

        DI

        CALL    SRL_UP

        EI

        POP     BC

        DJNZ    LP_MAIN

;

        RET

;--------------------------------------;

MK_STBL

        LD      HL,SCR_TBL

        LD      DE,#4000

        LD      B,#C0

LP_MSTB LD      (HL),E

        INC     HL

        LD      (HL),D

        INC     HL

;

        INC     D

        LD      A,D

        AND     #07

        JR      NZ,$+12

        LD      A,E

        ADD     A,#20

        LD      E,A

        JR      C,$+6

        LD      A,D

        SUB     #08

        LD      D,A

;

        LD      (HL),E

        INC     HL

        LD      (HL),D

        INC     HL

        DJNZ    LP_MSTB

;        RET

;--------------------------------------;

MKSRLUP

        LD      HL,SRL_UP

        LD      (HL),#ED  ;ld (...),sp

        INC     HL

        LD      (HL),#73

        INC     HL

        PUSH    HL

        INC     HL

        INC     HL

        LD      (HL),#01  ;ld bc,...

        INC     HL

        LD      (HL),#E0

        INC     HL

        LD      (HL),#17  ;ld bc,#17E0

        INC     HL

        LD      (HL),#31  ;ld sp,...

        INC     HL

        LD      DE,SCR_TBL

        LD      (HL),E

        INC     HL

        LD      (HL),D    ;ld sp,SCR_TBL

        INC     HL

        LD      (HL),#C3  ;jp ...

        INC     HL

        LD      D,H

        LD      E,L

        LD      BC,#0042+2

        ADD     HL,BC

        EX      DE,HL

        LD      (HL),E

        INC     HL

        LD      (HL),D ;jp lp_srup+#0042

        INC     HL

        PUSH    HL        ;lp_srup

        LD      C,LNS_NUM

LP_MSU0 LD      (HL),#D1  ;pop de

        INC     HL

        LD      (HL),#E1  ;pop hl

        INC     HL

        LD      B,#20

LP_MSU1 LD      (HL),#ED  ;ldi

        INC     HL

        LD      (HL),#A0

        INC     HL

        DJNZ    LP_MSU1

        DEC     C

        JR      NZ,LP_MSU0

        LD      (HL),#EA  ;jp pe,...

        INC     HL

        POP     DE

        LD      (HL),E

        INC     HL

        LD      (HL),D    ;jp pe,lp_srup

        INC     HL

        LD      (HL),#31  ;ld sp,...

        INC     HL

        LD      (HL),B    ;#00

        INC     HL

        LD      (HL),#58  ;ld sp,#5800

        INC     HL

        LD      B,#10

LP_MSU2 LD      (HL),#C5  ;push bc

        INC     HL

        DJNZ    LP_MSU2

        LD      (HL),#31  ;ld sp,_NULL

        INC     HL

        EX      DE,HL

        POP     HL        ;ld (...),sp

        LD      (HL),E    ;    ^^^

        INC     HL

        LD      (HL),D

        EX      DE,HL

        INC     HL

        INC     HL

        LD      (HL),#C9  ;ret

        RET

;--------------------------------------;

LN_SRUP EQU     LNS_NUM*#0042+#0027

;--------------------------------------;

_CODE   EQU     $

SRL_UP  EQU     _CODE          ;,LN_SRUP

CODEEND EQU     SRL_UP+LN_SRUP

;--------------------------------------;




RUNNING LINES ATTRIBUTES

;Attribute Running Line (lvd'96)

arline ld a, (alcnt)

dec a

jr nz, arl2

ld hl, (cure)

inc hl

ld a, (hl)

cp 0

jr nz, arl1

ld hl, text

ld a, (hl)

arl1 ld (cure), hl

ld l, a

ld h, 0

ld de, #3C00 ;font here

add hl, hl

add hl, hl

add hl, hl

add hl, de

ld de, albuff

ld bc, 8

ldir

ld a, 8

arl2 ld (alcnt), a

ld hl, #5A01

ld ix, albuff

ld b, 8

alldi ld c, h

ld d, c

ld e, l

dec e

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

ldi

rlc (ix+0)

inc ix

ex de, hl

jr nc, arl3

ld a, (coltxt)

ld (hl), a

jr alldic

arl3 ld a, (colbkg)

ld (hl), a

alldic inc hl

inc hl

djnz alldi

ret

coltxt db #78 ;color of letters

colbkg db #99 ;color of background

alcnt db 1

cure dw albuff+7

albuff db 0,0,0,0,0,0,0,0

text db "Runnin Line By Vadik (aka LVD) in feb.1996"

db 0





;attribute running line by L.Smirnov

        ORG #8000

        LD SP,#8000

        CALL INIT

        RES  5,(IY+1)

A1      HALT 

        CALL LINE

        BIT  5,(IY+1)

        JR   Z,A1

        RET 

LINE    LD   HL,#5A01

        LD   DE,#5A00

        LD   BC,255

        LDIR 

        LD   HL,FLAG

        RLC  (HL)

        JR   NC,L1

        LD   HL,(TEXT)

        INC  HL

        LD   (TEXT),HL

        LD   A,(HL)

        OR   A

        JR   NZ,L3

INIT    LD   A,1

        LD   (FLAG),A

        LD   HL,TXT

        LD   (TEXT),HL

        LD   A,(HL)

L3      LD   L,A

        LD   H,0

        ADD  HL,HL

        ADD  HL,HL

        ADD  HL,HL

        LD DE,#3C00 ; LD   DE,(#5C36)

        ADD  HL,DE

        LD   DE,BUFF

        LD   BC,8

        LDIR 

L1      LD   HL,#5A1F

        LD   DE,BUFF

        LD   C,32

        LD   A,8

L2      LD   (HL),#90 ;color 1

        EX   DE,HL

        RLC  (HL)

        EX   DE,HL

        JR   NC,L4

        LD   (HL),#FA ;color 2

L4      INC  DE

        ADD  HL,BC

        DEC  A

        JR   NZ,L2

        RET 

BUFF    DEFS 8

FLAG    DEFB 1

TEXT    DEFW TXT

TXT DEFB "Atribute scroller, Written by Smirnov Leonid 02.11.1996... ", 0



;from TASM4.12 RUNNING ATTR LINE BOLD

                ORG #8000

                LD SP,#6000

START           CALL ATTRLINE

                JP START

ATTRLINE        XOR     A

                INC     A

                LD      (atlin2+1), A

                LD      HL, attr_text

                LD      (atlin3+1), HL

atlin1          EI 

                HALT 

                LD      HL,#5901

                LD      DE,#5900

                LD      BC,#FF

                LDIR 

atlin2          LD      A, 0

                RRCA 

                LD      (atlin2+1), A

                LD      C, A

                JR      NC, atlfnt

atlin3          LD      HL, attr_text

                BIT     7, (HL)

                JR      Z, atlin4

                LD      HL, attr_text

atlin4          LD      A, (HL)

                INC     HL

                LD      (atlin3+1), HL

                LD      L, A

                LD      H, 0

                ADD     HL, HL

                ADD     HL, HL

                ADD     HL, HL

                LD      A, #3C

                ADD     A, H

                LD      H, A

                LD      (atlfnt+1), HL

atlfnt          LD      HL, #3D00

                LD      DE, #591F

atlin5          LD      A, (HL)

                RRCA 

                OR      (HL)

                INC     HL

                AND     C

                CP      1

                CCF 

                SBC     A, A

                LD      (DE), A

                LD      A, #20

                ADD     A, E

                LD      E, A

                JR      NC, atlin5

                BIT     5, (IY+1)

                JR      Z, atlin1

                RES     5, (IY+1)

                RET 

attr_text DB "DON-T SLEEP, MOTHERFUCKER... scroll from Tasm4.12"

                DB #FF



;by Alex Raider

                ORG #8000

                LD HL, TEXT

                LD A,(HL)

                OR #40

                LD (ldia0+1),A

                INC HL

                LD (ATRL+1),HL

LOOPATR         EI 

                HALT 

                CALL ATTRLIN

                JR LOOPATR

ATTRLIN         CALL    ATRLDI

ATREX           LD      A, 0

                DEC     A

                AND     7

                LD      (ATREX+1), A

                RET     NZ

ATRL            LD      HL, 0

                LD      A, (HL)

                AND     A

                JR      NZ, ATRPRN

                RET 

ATRPRN          INC     HL

                LD      (ATRL+1), HL

                SUB     #20

                ADD     A, A

                LD      H, 0

                LD      L, A

                ADD     HL, HL

                ADD     HL, HL

                LD      A, H

                ADD     A, #3D

                LD      H, A

                LD      DE, buff8

                LD      A, (HL)

                RRCA 

            ;   OR      (HL)

                LD      (DE), A

                INC     L

                INC     DE

                LD      A, (HL)

                RRCA 

            ;   OR      (HL)

                LD      (DE), A

                INC     L

                INC     DE

                LD      A, (HL)

                RRCA 

                OR      (HL)

                LD      (DE), A

                INC     L

                INC     DE

                LD      A, (HL)

                RRCA 

                OR      (HL)

                LD      (DE), A

                INC     L

                INC     DE

                LD      A, (HL)

                RLCA 

                OR      (HL)

                LD      (DE), A

                INC     L

                INC     DE

                LD      A, (HL)

                RLCA 

                OR      (HL)

                LD      (DE), A

                INC     L

                INC     DE

                LD      A, (HL)

                RLCA 

                OR      (HL)

                LD      (DE), A

                INC     L

                INC     DE

                LD      A, (HL)

                RLCA 

                OR      (HL)

                LD      (DE), A

                INC     L

                INC     DE

                RET 

buff8           DB 0,0,0,0,0,0,0,0

ATRLDI          LD      IX, buff8

                LD      DE, #5A00

                LD      HL, #5A01

                LD      LY, 8;          ;vysota

ldiloop         LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                LDI 

                SLA     (IX+0)

                INC     IX

                LD      A, #31  ;color backgnd

                JR      NC, ldiex

ldia0           LD      A, 0

ldiex           LD      (DE), A

                INC     HL

                INC     DE

                DEC     LY;YL

                JP      NZ, ldiloop

                RET 

TEXT    DB #81; color of letters

       DB "ATTR SCROLLER from FLASH Inc intro by ALEX RAIDER......some modified :)    "

       ;DB 0




CIRCLES

;--------------------------------------;

;     CIRCLE AND TEXTURED FILL         ;

;      special for KF-STUDIO           ;

;(c) SerzhSoft, Shadrinsk, 31 july 1997;

;--------------------------------------;

        ORG     #8000

        JP      EXAMPLE

;--------------------------------------;

;используется в изменяемых командах

_NULL   EQU     0

;--------------------------------------;

;адрес текстуры для вывода

TXRADR  DW      TEXTURE

;--------------------------------------;

ATTR_P  EQU     23693 ;атрибуты экрана

;--------------------------------------;

;биты:   0 - over 0/1,   2 - inverse 0/1

;      4 - ink&paper 9 (прозрачные атр.)

P_FLAG  EQU     23697

;--------------------------------------;

;демонстрационный пример

EXAMPLE

        XOR     A

        LD      (P_FLAG),A

;

        ld      hl,-100

        ld      de,-100

        ld      a,255

        CALL    CIRCLE

;

        LD      E,10

        LD      D,10

        XOR     A

        CALL    TXRFILL

;

        ld      hl,200

        ld      de,100

        ld      a,50

        CALL    CIRCLE

;

        LD      E,200

        LD      D,100

        LD      A,1

        CALL    TXRFILL

;

        ld      hl,115

        ld      de,150

        ld      a,20

        CALL    CIRCLE

;

        LD      E,115

        LD      D,150

        LD      A,2

        CALL    TXRFILL

;

        LD      A,#04 ;[set 2,a]

        LD      (P_FLAG),A ;inverse 1

;

        LD      E,10

        LD      D,170

        LD      A,2

        CALL    TXRFILL

;

        CALL    WAITKEY

        RET

;--------------------------------------;

;ждет нажатия на любую клавишу

WAITKEY

        XOR     A

        IN      A,(#FE)

        CPL

        AND     #1F

        JR      Z,WAITKEY

        RET

;--------------------------------------;

;Algorythm designed by SerzhSoft (c)1996

;рисование окружности

; HL=x, DE=y (-32768..+32767)

; A=радиус (0..255) ;при 0 - точка

CIRCLE

        ld      (X_CIRC),hl

        ld      (Y_CIRC),de

        ld      e,a

        ld      c,#00

        ld      b,c

        ld      d,c

        srl     a

LPCIR1  ex      af,af'

LPCIR2  call    PUT8PX

        inc     c

        ex      af,af'

        sub     c

        jr      nc,LPCIR1

        dec     e

        add     a,e

        ex      af,af'

        ld      a,e

        cp      c

        jr      nc,LPCIR2

        ret

;

PUT8PX  call    PUT4PX

;

PUT4PX  ld      a,c

        ld      c,e

        ld      e,a

        ld      hl,_NULL

X_CIRC  equ     $-2

        push    hl

        add     hl,bc

        call    PUT2PX

        pop     hl

        sbc     hl,bc

;

PUT2PX  inc     h

        dec     h

        ret     nz

        ld      a,l

        ld      (X_NEW),a

        ld      hl,_NULL

Y_CIRC  equ     $-2

        push    hl

        add     hl,de

        call    PUT1PX

        pop     hl

        sbc     hl,de

;

PUT1PX  inc     h

        dec     h

        ret     nz

        ld      a,l

        cp      #C0

        ret     nc

        push    de

        ld      d,a

        ld      e,_NULL

X_NEW   equ     $-1

        push    bc

        call    PLOT

        pop     bc

        pop     de

        ret

;--------------------------------------;

;текстурная заливка

; E=x, D=y, A=номер текстуры (0..255)

TXRFILL

        RRCA

        RRCA

        RRCA

        LD      H,A

        AND     #E0

        LD      L,A

        XOR     H

        ADD     A,TEXTURE/256 ;A,'TEXTURE

        LD      H,A

        LD      (TXRADR),HL

;

;заливка, но не по номеру текстуры, а

;по адресу, установленному в TXRADR

TXRFIL2

        LD      A,D

        CP      #C0

        RET     NC

        LD      A,(P_FLAG)

        BIT     2,A

        LD      A,#A9 ;[xor c] - inv. 0

        JR      Z,IF_TXF0

        LD      A,#A1 ;[and c] - inv. 1

IF_TXF0 LD      (INV_TXF),A

        LD      HL,BUFFER

        LD      A,#FF

        PUSH    AF

        PUSH    DE

LP_TXF1 POP     DE

        INC     D

        JP      Z,GO_TXF0

        DEC     D

        CALL    POINTHL

        JR      NZ,LP_TXF1

        EX      AF,AF'

LP_TXF2 LD      A,E

        LD      (HL),A

        OR      A

        JR      Z,GO_TXF1

        DEC     E

        CALL    POINTHL

        JR      Z,LP_TXF2

LP_TXF3 INC     E

        JR      Z,GO_TXF8

GO_TXF1 PUSH    HL

        CALL    POINT

        JR      NZ,GO_TXF7

        LD      A,(HL)

        OR      C

        CALL    TO_PLOT

        POP     HL

        LD      A,D

        OR      A

        JR      Z,GO_TXF4

        DEC     D

        CALL    POINTHL

        JR      Z,GO_TXF2

        EX      AF,AF'

        LD      A,B

        JR      GO_TXF3

GO_TXF2 EX      AF,AF'

        INC     A

        DEC     A

        JR      NZ,GO_TXF3

        LD      A,C

        PUSH    DE

GO_TXF3 EX      AF,AF'

        INC     D

GO_TXF4 LD      A,D

        CP      #BF

        JR      NC,LP_TXF3

        INC     D

        CALL    POINTHL

        JR      Z,GO_TXF5

        EX      AF,AF'

        AND     A

        JR      GO_TXF6

GO_TXF5 EX      AF,AF'

        JR      C,GO_TXF6

        SCF

        PUSH    DE

GO_TXF6 EX      AF,AF'

        DEC     D

        JR      LP_TXF3

GO_TXF7 POP     HL

GO_TXF8 LD      A,E

        SUB     (HL)

        INC     HL

        LD      (HL),D

        INC     HL

        LD      (HL),A

        INC     HL

        JR      LP_TXF1

;

LP_TXF4 ADD     HL,DE

        DEC     HL

        LD      A,(HL)

        DEC     HL

        LD      D,(HL)

        DEC     HL

        LD      E,(HL)

        PUSH    HL

        PUSH    AF

        CALL    POINT

        POP     AF

        LD      B,A

        LD      A,E

        RRA

        RRA

        RRA

        RRA

        LD      A,D

        RLA

        LD      DE,(TXRADR)

        XOR     E

        AND     #1F

        XOR     E

        LD      E,A

LP_TXF5 LD      A,(DE)

        AND     C

INV_TXF XOR     C

        XOR     (HL)

        LD      (HL),A

        RRC     C

        JR      NC,GO_TXF9

        INC     L

        LD      A,E

        XOR     #01

        LD      E,A

GO_TXF9 DJNZ    LP_TXF5

        POP     HL

GO_TXF0 LD      DE,BUFFER

        AND     A

        SBC     HL,DE

        JR      NZ,LP_TXF4

        RET

;--------------------------------------;

;выч. адреса и проверка состояния точки

POINT

        LD      B,#07

        LD      A,D

        RRA

        SCF

        RRA

        RRA

        AND     #5F

        LD      H,A

        XOR     E

        AND     B

        XOR     E

        RRCA

        RRCA

        RRCA

        LD      L,A

        LD      A,D

        XOR     H

        AND     B

        XOR     H

        LD      H,A

        LD      A,E

        AND     B

        LD      B,A

        LD      A,#80

        JR      Z,GO_PNT

LP_PNT  RRCA

        DJNZ    LP_PNT

GO_PNT  LD      C,A

        AND     (HL)

        RET

;

POINTHL

        PUSH    HL

        CALL    POINT

        POP     HL

        RET

;--------------------------------------;

;установка точки

PLOT

        CALL    POINT

        LD      A,(P_FLAG)

        PUSH    HL

        LD      HL,#A9A9 ;over 1, inv. 1

        BIT     0,A

        JR      NZ,IF_PLT1

        LD      L,#B1 ;[or c] - over 0

IF_PLT1 BIT     2,A

        JR      NZ,IF_PLT2

        LD      H,#00 ;[nop] - inverse 1

IF_PLT2 LD      (OVR_PLT),HL

        POP     HL

        LD      A,(HL)

OVR_PLT OR      C

INV_PLT XOR     C

;

TO_PLOT ;вход для пр-ры заливки и др.

        LD      (HL),A

        LD      A,(P_FLAG)

        BIT     4,A

        RET     NZ

        LD      A,H

        RRA

        RRA

        RRA

        AND     #03

        OR      #58

        LD      H,A

        LD      A,(ATTR_P)

        LD      (HL),A

        RET

;--------------------------------------;

;выравнивание на сегмент (граница 256)

        DS      $/256*256+256-$

;--------------------------------------;

;различные текстуры 2*2 знакоместа

TEXTURE

        DB      %11111111,%11110000

        DB      %11111111,%11111000

        DB      %11000000,%00110100

        DB      %11011111,%11111010

        DB      %11011111,%11110100

        DB      %11011010,%11111010

        DB      %11011101,%11110100

        DB      %11011010,%11111010

        DB      %11011111,%11110100

        DB      %11011111,%11111010

        DB      %11111111,%11110100

        DB      %11111111,%11111010

        DB      %01010101,%01010100

        DB      %00101010,%10101010

        DB      %00010101,%01010100

        DB      %00000000,%00000000

;

        DB      %11111111,%11110000

        DB      %11111111,%11111000

        DB      %11000000,%00110100

        DB      %11011111,%11111010

        DB      %11011111,%11110100

        DB      %11011000,%11111010

        DB      %11011010,%11110100

        DB      %11011000,%11111010

        DB      %11011111,%11110100

        DB      %11011111,%11111010

        DB      %11111111,%11110100

        DB      %11111111,%11111010

        DB      %01010101,%01010100

        DB      %00101010,%10101010

        DB      %00010101,%01010100

        DB      %00000000,%00000000

;

        DB      %11111111,%11110000

        DB      %11111111,%11111000

        DB      %11000000,%00110100

        DB      %11011111,%11111010

        DB      %11011111,%11110100

        DB      %11011111,%11111010

        DB      %11011111,%11110100

        DB      %11011111,%11111010

        DB      %11011111,%11110100

        DB      %11011111,%11111010

        DB      %11111111,%11110100

        DB      %11111111,%11111010

        DB      %01010101,%01010100

        DB      %00101010,%10101010

        DB      %00010101,%01010100

        DB      %00000000,%00000000

;--------------------------------------;

;буфер для запоминания координат линий

; при заливке (требует большого объема)

BUFFER  EQU     $

;--------------------------------------;


MUSIC ANALYSERS

;горизонтальный аттрибутный аналайзер с Барнаульских дисков

        ORG #6000

        CALL MUS ;INIT MUSIC PLAYER

        EI 

ZA      HALT 

        CALL MUS+5 ;PLAY MUSIC

        CALL MEFF       ;DRAW ANALYSER

        IN A,(#FE)

        CPL 

        AND #1F

        JR Z,ZA

        CALL MUS

        RET 


MEFF    LD HL,#5800 ;chan a pos

        LD A,8

        CALL L1

        LD HL,#5820 ;chan b pos

        LD A,9

        CALL L1

        LD HL,#5840 ;chan c pos

        LD A,10

        CALL L1

        RET 

L1      LD BC,65533

        OUT (C),A

        IN A,(C)

        AND 15

        AND A

        JR Z,PS1

        LD B,A

        EX AF,AF'

        LD A,#FF

LL1     LD (HL),A

        INC L

        DJNZ LL1

        EX AF,AF'

        SUB 15

        NEG 

        AND A

        JR Z,PS1

        LD B,A

        LD A,7

LL2     LD (HL),A

        INC L

        DJNZ LL2

PS1     RET 


MUS     INCBIN "Music with player here






to do......


RUNNING LINES 8x8

NEDOOS

ESXDOS

GENERAL SOUND

PRINT 4x8