; macros.asm

; NXtel is copyright © 2018-2020 Robin Verhagen-Guest.
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program.  If not, see:
; https://github.com/Threetwosevensixseven/NXtel/blob/master/LICENSE
;
; NXtel source code for the Spectrum Next client, server, page manager
; is available at: https://github.com/Threetwosevensixseven/NXtel

VersionPrefix           equ "1.0.0"

include "version.asm", 1                                ; Auto-generated by ..\build\deploy.bat

VersionOnly             macro()
                        db "v", VersionPrefix, "."
                        BuildNo()
mend



VersionOnlyValue        equ "v"+VersionPrefix+"."+BuildNoValue
VersionOnlyWidth        equ FWV+FW0+FWFullStop+FW1+FWFullStop+BuildNoWidth

Rst8                    macro(Command)
                        if NoDivMMC
                          nop                           ; take up same number of bytes
                          or a                          ; and clear carry to indicate success
                        else
                          rst $08
                          noflow
                          db Command
                        endif
mend


VersionTimeValue        equ VersionOnlyValue + " " + BuildTimeValue


PrintULAString          macro(Text, Length)
                        ld a, 2                         ; upper screen
                        call 5633                       ; open channel
Loop:                   ld de, Text                     ; address of string
                        ld bc, Length                   ; length of string to print
                        call 8252                       ; print our string
                        //jp Loop                         ; repeat until screen is full
mend



Turbo                   macro(Mode)
                        nextreg TurboRegister, Mode
mend



Border                  macro(Colour)
                        if Colour=0
                          xor a
                        else
                          ld a, Colour
                        endif
                        out (ULA_PORT), a
                        if Colour=0
                          xor a
                        else
                          ld a, Colour*8
                        endif
                        ld (23624), a
mend



PortOut                 macro(Port, Value)
                        ld bc, Port
                        ld a, Value
                        out (c), a
mend


DebugPrintA             macro()
                        jp BEGIN
DEBUG_VAL:              db 0
BEGIN:
                        ld (DEBUG_VAL), a
                        ld a, 2                         ; upper screen
                        call 5633                       ; open channel
                        ld a, (DEBUG_VAL)
                        ld c, a
                        ld b, 0                         ; Print this byte
                        call 6683                       ; As a decimal
                        ld a, 32
                        rst 16
mend



ULAPrintSetup           macro(Colour)
                        ld a, 2                         ; upper screen
                        call 5633                       ; open channel
                        ld a, Colour
                        ld (23693), a
                        ld (23695), a
mend



ULAPrint                macro(Char)
                        ld a, Char
                        rst 16
                        ld a, 255
                        ld(23692), a                    ; Turn off ULA scroll
mend



ULAScreen               macro(Shadow, ReEnableInterrupts)
                        if (Shadow)
                          ld a, $18
                        else
                          ld a, $10
                        endif
                        or [WhichULAScreen]SMC
                        ld bc, $7FFD
                        out (c), a
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



PageBankZX              macro(Bank, ReEnableInterrupts)
                        ld bc, $7FFD
                        di
                        ld a, (Bank & 7) | 16
                        out (c), a
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



ClsAttrFull             macro(Colour)
                        if Colour = DimBlackBlackP
                         xor a
                        else
                          ld a, Colour
                        endif
                        ld hl, ATTRS_8x8
                        ld (hl), a
                        ld de, ATTRS_8x8+1
                        ld bc, ATTRS_8x8_COUNT-1
                        ldir
mend



NextPaletteRGB8         macro(Index, RGB8, Pal)
                        ld de, Index+(RGB8*256)
                        ld a, Pal
                        ld (NextPaletteRGB8Proc.Palette), a
                        call NextPaletteRGB8Proc
mend


FillLDIR                macro(SourceAddr, Size, Value)
                        ld a, Value
                        ld hl, SourceAddr
                        ld (hl), a
                        ld de, SourceAddr+1
                        ld bc, Size-1
                        ldir
mend



MMU0                    macro(Bank, ReEnableInterrupts)
                        di
                        nextreg $50, Bank
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



MMU1                    macro(Bank, ReEnableInterrupts)
                        di
                        nextreg $51, Bank
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



MMU2                    macro(Bank, ReEnableInterrupts)
                        di
                        nextreg $52, Bank
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



MMU3                    macro(Bank, ReEnableInterrupts)
                        di
                        nextreg $53, Bank
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



MMU4                    macro(Bank, ReEnableInterrupts)
                        di
                        nextreg $54, Bank
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



MMU5                    macro(Bank, ReEnableInterrupts)
                        di
                        nextreg $55, Bank
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



MMU6                    macro(Bank, ReEnableInterrupts)
                        di
                        nextreg $56, Bank
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



MMU7                    macro(Bank, ReEnableInterrupts)
                        di
                        nextreg $57, Bank
                        if (ReEnableInterrupts)
                          ei
                        endif
mend



SetSpritePattern        macro(Address, NextPatternNo, DataPatternNo)
                        ld hl, Address+(DataPatternNo*256)
                        ld a, NextPatternNo
                        call WriteSpritePattern
mend



NextSprite              macro(ID, u16X, u8Y, PaletteOffset, bMirrorX, bMirrorY, bRotate, bVisible, Pattern)
                        ; Port $303B, if written, defines the sprite slot to be configured by ports $57 and $5B,
                        ; and also initializes the address of the palette.
                        ; Port $57 is write-only and is used to send the attributes of the selected sprite slot,
                        ; being the address is auto-incremented each writing and after sending the 4 bytes of
                        ; attributes the address points to the next sprite. The description of each byte follows below:
                        ;   1st: X position (bits 7-0).
                        ;   2nd: Y position (0-255).
                        ;   3rd: bits 7-4 is palette offset, bit 3 is X mirror, bit 2 is Y mirror,
                        ;        bit 1 is rotate flag and bit 0 is X MSB.
                        ;   4th: bit 7 is visible flag, bit 6 is reserved, bits 5-0 is Name (pattern index, 0-63).
                        B1 = low(u16X+32);
                        B2 = (u8Y+32) and %11111111
                        B3a = (PaletteOffset and %1111) shl 4           ; OOOOxxxx
                        B3b = (bMirrorX and %1) shl 3                   ; xxxxXxxx
                        B3c = (bMirrorY and %1) shl 2                   ; xxxxxYxx
                        B3d = (bRotate  and %1) shl 1                   ; xxxxxxRx
                        B3e = (((u16X+32) and %1 00000000) shr 8) and %1; xxxxxxxM
                        B3 = B3a+B3b+B3c+B3d+B3e                        ; OOOOXYRM
                        B4a = (bVisible and %1) shl 7                   ; Vxxxxxxx
                        B4b = Pattern and %111111                       ; xxPPPPPP
                        B4 = B4a+B4b                                    ; VxPPPPPP
                        ld a, ID and %111111
                        ld hl, B1+(B2*256)
                        ld de, B3+(B4*256)
                        call NextSpriteProc
mend



FillLDIR                macro(SourceAddr, Size, Value)
                        ld a, Value
                        ld hl, SourceAddr
                        ld (hl), a
                        ld de, SourceAddr+1
                        ld bc, Size-1
                        ldir
mend



PageLayer2Bottom48K     macro(Page, Switch)
                        nextreg $50, (Page*2)+0         ; MMU page bottom 48K to layer 2
                        nextreg $51, (Page*2)+1
                        nextreg $52, (Page*2)+2
                        nextreg $53, (Page*2)+3
                        nextreg $54, (Page*2)+4
                        nextreg $55, (Page*2)+5
                        if Switch
                          nextreg $12, Page               ; Set layer 2 base page
                        endif
mend



PageResetBottom48K      macro()
                        nextreg $50,255                 ; MMU page bottom 48K back
                        nextreg $51,255
                        nextreg $52, 13
                        nextreg $53, 12                 ; This is non-standard - the bottom half of 16K page 6
                        nextreg $54,  4
                        nextreg $55,  5
mend


PageResetBottom48KStd   macro()
                        nextreg $50,255                 ; MMU page bottom 48K back
                        nextreg $51,255
                        nextreg $52, 10
                        nextreg $53, 11
                        nextreg $54,  4
                        nextreg $55,  5
mend



CpHL                    macro(Register)
                        or a
                        sbc hl, Register
                        add hl, Register
mend



ClsAttrFull             macro(Colour)
                        if Colour = DimBlackBlackP
                         xor a
                        else
                          ld a, Colour
                        endif
                        ld hl, ATTRS_8x8
                        ld (hl), a
                        ld de, ATTRS_8x8+1
                        ld bc, ATTRS_8x8_COUNT-1
                        ldir
mend



Contention              macro(OnOff)
                        NextRegRead(8)
                        if OnOff
                          and %1011 1111
                        else
                          or %0100 0000
                        endif
                        nextreg 8, a
mend



NextRegRead             macro(Register)
                        ld bc, Sprite_Register_Port     ; Sprite_Register_Port = $243B
                        ld a, Register
                        out (c), a
                        inc b
                        in a, (c)
mend



MFBreak                 macro()
                        push af
                        ld a, r
                        di
                        in a, ($3f)
                        rst 8
mend



PadString               macro(String, Len)
                        db String
                        ds Len - length(String), 0
mend



PadStringLeftSpaces     macro(String, Len)
                        ds Len - length(String), 32
                        db String
mend



Pause                   macro(Frames)
                        ld hl, Frames
                        ld (PauseProc.Timer), hl
                        call PauseProc
mend



/*Delay                   macro(Time)
                        ld hl, Time
                        call DelayProc
mend*/



Freeze                  macro()
Loop:                   Border(Red)
                        Border(Blue)
                        jp Loop
mend



Frz                     macro(Colour1, Colour2)
Loop:                   Border(Colour1)
                        Border(Colour2)
                        jp Loop
mend



FlipScreen              macro()
                        if ULAMonochrome
                          call FlipULAScreen
                        endif
mend



ResetLastKeypress       macro()
                        xor a
                        ld (ReadKeyOLD.LastKey), a

mend


/*
ESPLogInit              macro()
                        if enabled LogESP
                          nextreg $55, 32
                          FillLDIR($A000, $2000, $00)     ; Clear ESP log
                          ld hl, ESPLogStart
                          ld (ESPLogPointer), hl
                          ld hl, 0
                          ld (ESPLogLen), hl
                        endif
mend



ESPLog                  macro()
                        if enabled LogESP
                          call ESPLogProc
                        endif
mend



ESPLogText              macro(Text)
                        if enabled LogESP
                          for n := 1 to length(Text)
                            ld a, Text[n]
                            call ESPLogProc
                          next;n
                        endif
mend
*/


TSHeaderMatch           macro(Char)
                        ld a, Char
                        cpi
                        jp nz, DetectTSHeader.NotTSHeader
mend



TSHeaderFind            macro(Char)
                        ld a, Char
                        cpir
                        jp nz, DetectTSHeader.NotTSHeader
mend



TSHeaderBodySkip        macro(SkipCount)
                        loop SkipCount
                          inc hl
                          dec bc
                        lend
mend



TSBodyMatch             macro(Char)
                        ld a, Char
                        cpi
                        jp nz, CaptureTSFrame6.NotTSBody
mend



TSBodyFind              macro(Text)
                        push hl
                        ld hl, Buffer
                        ld (TSBodyFindProc.SearchText), hl
                        pop hl
                        call TSBodyFindProc
                        jp Continue
Buffer:                 dbl Text
Continue:
mend



DecodeDecimal           macro(Buffer, DigitCount)
                        ld hl, Buffer
                        dec hl
                        ld (DecodeDecimalProc.DecimalBuffer), hl
                        ld b, DigitCount
                        call DecodeDecimalProc
mend



EnableKeyboardScan      macro(Enable)
                        if Enable
                          ld a, $CD                     ; $CD (call nnnn)
                        else
                          xor a
                          ld (KB.CharsAvailable), a
                          ld a, $21                     ; $21 (ld hl, nnnn)
                        endif
                        ld (EnableDisableKBScan), a
                        if not Enable
                          ld hl, (KB.WritePointer)
                          ld (KB.ReadPointer), hl
                        endif

mend



EnableCaptureTSFrame    macro(Enable)
                        if Enable
                          ld a, $CD                     ; $CD (call nnnn)
                        else
                          ld a, $21                     ; $21 (ld hl, nnnn)
                        endif
                        ld (ESPConnect.CaptureTSFrameOrNot), a
mend



SendCharWaitOK          macro(Char)
                        ld a, Char
                        ld (SendCharWaitOKProc.CharToSend), a
                        call SendCharWaitOKProc
mend



StatusIcon              macro(StatusIconNumber)
                        ld e, StatusIconNumber
                        call StatusIconProc
mend



PlaySFX                 macro(SFXNumber)
                        ld e, SFXNumber
                        call PlaySFXProc
mend



MFBreak                 macro()
                        push af                         ; This sequence triggers the Debug menu
                        ld a, r                         ; on the Next Multiface replacement
                        di                              ; when booted into NextZXOS.
                        in a, ($3F)                     ; The MF must have been activated and returned from
                        rst $8                          ; once, in order for this code to trigger a breakpoint.
mend



CallP3DOS               macro(CallAddress, Bank, EnableInterrupts)
                        NextRegRead($54)
                        ld (Slot4), a
                        nextreg $54, 4
                        exx
                        ld de, CallAddress
                        ld c, Bank
                        call P3DOSCaller
                        nextreg $54, [Slot4]SMC
                        if (EnableInterrupts)
                          ei
                        endif
mend



DisplayBrowser          macro()
                        ld hl, BrowserData.FileTypes
                        ld de, BrowserData.Text
                        ld a, $3F
                        CallP3DOS(IDE_BROWSER, 7, true)
mend



mMarkBank               macro(n)
                        disp 0
                        org zeusmmu(n*2): ds $2000, n*2 ; Fill up 8k bank nn with nn
                        org zeusmmu((n*2)+1): ds $2000, (n*2)+1;
                        output_bin BankName(n), zeusmmu(n*2), $4000;
mend



mUnmarkBank             macro(n)
                        zeusmarkunused zeusmmu(n*2),$2000
                        zeusmarkunused zeusmmu((n*2)+1),$2000
mend



EnableTime              macro(Enable, ReenableInterrupts)
                        if Enable
                          ld a, 1
                        else
                          xor a
                        endif
                        ld (GetTime.ShowClock), a
                        if Enable
                          ld a, $CD                     ; call NNNN
                        else
                          ld a, $21                     ; ld hl, NNNN
                        endif
                        ld (PrintTimeCall), a
mend



[[
function TeletextColour(Value)
begin
  if (Value mod 8) = 0
    return %000 000 00                                  ; 0  Black    The layer 2 primary palette repeats these
  if (Value mod 8) = 1
    return %111 000 00                                  ; 1  Red      8 colours 32 times through indices 0..255.
  if (Value mod 8) = 2
    return %000 111 00                                  ; 2  Green
  if (Value mod 8) = 3
    return %111 111 00                                  ; 3  Yellow
  if (Value mod 8) = 4
    return %001 001 11                                  ; 4  Blue     Lightened for readability on black background.
  if (Value mod 8) = 5
    return %111 001 11                                  ; 5  Magenta  Uses $E7 because global transparency is $E3.
  if (Value mod 8) = 6
    return %000 111 11                                  ; 6  Cyan
  return %111 111 11                                    ; 7  White
end
]]



CopperNOP               macro(Iterations)
                        ds Iterations*2
mend



CopperHalt              macro()
                        dw $FFFF
mend



CopperWait              macro(X, Y, Frames)
                        for n = Y+Frames-1 to Y step -1
                          db %10000000 | ((X & %111111) << 1) | ((high n) & %1)
                          db low n
                        next ;n
mend



CopperMove              macro(Reg, Val, Iterations)
                        for n = 1 to Iterations
                          db Reg & %01111111
                          db Val & %11111111
                        next ;n
mend



CopperPalette           macro(Iterations, Each)
                        for n = 0 to Iterations - 1
                          db $41                        ; 8-bit palette register
                          db TeletextColour(n / Each)   ; 8-bit teletext colour value (RRRGGGBB)
                        next ;n
mend



CopperControl           macro(Command, Position)
                        nextreg $62, ((Command & %11) << 6) | ((high Position) & %111)
                        nextreg $61, low Position
mend



CSBreak                 macro()                         ; Intended for CSpect debugging
                        push bc                         ; enabled when the -brk switch is supplied
                        noflow                          ; Mitigate the worst effect of running on real hardware
                        db $DD, $01                     ; On real Z80 or Z80N, this does NOP:LD BC, NNNN
                        nop                             ; so we set safe values for NN
                        nop                             ; and NN,
                        pop bc                          ; then we restore the value of bc we saved earlier
mend



CSExit                  macro()                         ; Intended for CSpect debugging
                        noflow                          ; enabled when the -exit switch is supplied
                        db $DD, $00                     ; This executes as NOP:NOP on real hardware
mend



[[
function Layer2Addr(X, Y)
begin
  return (8+(X*6))+((Y*8)*256)
end

function DisplayBufferAddr(X, Y)
begin
  return DisplayBuffer+(X+(Y*40))
end

function PrintTimeFrameCount()
begin
  if ULAMonochrome
    return 51
  else
   return 11
  end
end
]]

