Sunday, June 04, 2017

ZX Spectrum Next - Bitmap example disassembly

I've been having a poke around in the ZX Spectrum Next Bitmap example. It's mostly clear, but one or two things are....odd.
This is what I have so far.

UPDATE: Banking registers now confirmed by Jim Bagley

; Disassembly of the file "bitmaps\BMPLOAD"
; 
; BMP Port $123b
;     bit 0    -    Write enable. (banks in 16K from $0000-$3fff)
;     bit 1    -    bitmap ON
;     bit 4    -    Layer 2 below spectrum screen
;     bit 6-7  -    Which 16K bank to page into $0000-$3ffff
;
; On entry, HL = arguments
;

;
; Since dot commands run at $2000, and the Layer 2 is paged into $0000-$3fff
; It needs to have some code above the lower 16K to actually copy into layer 2
; So copy up the "copy" routine, and it'll page in/out the layer 2 bank
;
2000 226221    ld      (HL_Save),hl     ; Store HL
2003 216421    ld      hl,UploadCode    ; Src  = 
2006 110060    ld      de,6000h         ; dest = $6000
2009 011f00    ld      bc,001fh         ; size = 31 bytes
200c edb0      ldir    

200e 2a6221    ld      hl,(HL_Save)     ; get HL back
2011 7c        ld      a,h
2012 b5        or      l                ; is hl 0?
2013 2008      jr      nz,201dh         ; if we have an argument, carry on

2015 213121    ld      hl,2131h         ; get message
2018 cd1d21    call    PrintText        ; print the filename
201b 1823      jr      Exit             ; exit

; Load file....?
CopyFilename:
201d 112421    ld      de,FileName      ; "filename.ext" text - space to store filename?
2020 060c      ld      b,0ch            ; b = $0c (max length of allowed filename - no path it seems)

; Copy, and validate characters in filename
2022 7e        ld      a,(hl)           ; first byte of filename
2023 fe3a      cp      3ah              ; is it a ":"?  End of filename
2025 2810      jr      z,EndFilename    ; if so end of filename  
2027 b7        or      a                ; 0?
2028 280d      jr      z,EndFilename    ; if so end of filename
202a fe0d      cp      0dh              ; newline?
202c 2809      jr      z,EndFilename    ; if so end of filename
202e cb7f      bit     7,a              ; over 127?
2030 2005      jr      nz,EndFilename   ; if so end of filename
2032 12        ld      (de),a           ; copy over to filename cache
2033 23        inc     hl               ; next src letter
2034 13        inc     de               ; next dest letter
2035 10eb      djnz    2022h            ; copy all
EndFilename
2037 af        xor     a                ; Mark end of filename
2038 12        ld      (de),a           ; store filename
2039 dd212421  ld      ix,FileName      ; get filename base address
203d cd6220    call    LoadFile:

Exit:
2040 af        xor     a                ; no error
2041 c9        ret                      ; exit


; esxDOS detect if unit is ready
DetectUnit:
2042 af        xor     a                ; Detect if unit is ready
2043 cf        rst     08h              ; call esxDOS
2044 89        db      $89              ; M_GETSETDRV

; Open
; IX= Filename (ASCIIZ)
; B = FA_READ       ($01)
;     FA_APPEND     ($06)
;     FA_OVERWRITE  ($0C)
;
OpenFile:
2045 dde5      push    ix               ; ix = filename
2047 e1        pop     hl               ; If a DOT command (rather than normal memory), uses HL not IX
2048 0601      ld      b,01h            ; b = FA_READ
204a 3e2a      ld      a,2ah            ; a = unknown  (a=0 for open)
204c cf        rst     08h              ; call esxDOS
204d 9a        db      $9a              ; F_Open
204e 325620    ld      (2056h),a        ; Store file handle  (self modify read from file code)
2051 c9        ret                      ;


; esxDOS command - Read from file - command $9d
; IX = address to load into
; BC = number of bytes to load
; A  = file handle
ReadBytes:
2052 dde5      push    ix               ; IX = where to to store data
2054 e1        pop     hl               ; get address to load into
2055 3e00      ld      a,00h            ; $00 is self modified
2057 cf        rst     08h              ; call esxDOS
2058 9d        db      $9d              ; F_Read
2059 c9        ret     

; esxDOS command - Close File - command $9b
; A = file handle
CloseFile:
205a 3a5620    ld      a,(2056h)         ; Get open file handle 
205d b7        or      a                 ; is it 0? (did it open)
205e c8        ret     z                 ; if file handle is 0, return
205f cf        rst     08h               ; Call esxDOS
2060 9b        db      $9b               ; F_Close
2061 c9        ret     

LoadFile:
2062 dde5      push    ix                ; remember filename
2064 cd4220    call    DetectUnit        ; detect unit and open...?!?!?
2067 dde1      pop     ix                ; get filename back
2069 cd4520    call    OpenFile          ; OpenFile - again??
206c dd21e720  ld      ix,BMPHeader      ; Read the BMP file header
2070 013600    ld      bc,0036h          ; read header ($36 bytes)
2073 cd5220    call    ReadBytes
2076 dd218321  ld      ix,BitMap         ; read block into $2183
207a 010004    ld      bc,0400h          ; read palette - 1024 bytes
207d cd5220    call    ReadBytes

;
; Convert the 24bit palette into a simple RRRGGGBB format
;
ConvertBMP:
2080 218321    ld      hl,BitMap         ; Get buffer address ($2183)
2083 11003f    ld      de,3f00h          ; Dest address of converted palette
2086 0600      ld      b,00h

ConvertionLoop:
2088 7e        ld      a,(hl)            ; get BLUE byte
2089 23        inc     hl                ; move on to green
208a c620      add     a,20h             ; brighten up a bit? (blue is always pretty dark??)
208c 3002      jr      nc,SkipSatB       ; overflow? 
208e 3eff      ld      a,0ffh            ; if overflow, then saturate to $FF

SkipSatB:
2090 1f        rra                       ; get top 2 bits only  RRRGGGBB
2091 1f        rra     
2092 1f        rra     
2093 1f        rra     
2094 1f        rra     
2095 1f        rra     
2096 e603      and     03h               ; and store them at the bottom
2098 4f        ld      c,a               ; c holds current byte

2099 7e        ld      a,(hl)            ; get GREEN
209a 23        inc     hl                ; move onto red
209b c610      add     a,10h             ; brighten up a bit as well
209d 3002      jr      nc,SkipSatG       ; if no overflow, skip saturate
209f 3eff      ld      a,0ffh

SkipSatG:
20a1 1f        rra                       ; get 3 bits of green into right place
20a2 1f        rra     
20a3 1f        rra     
20a4 e61c      and     1ch               ; mask off remaining lower bits
20a6 b1        or      c                 ; merge with output byte
20a7 4f        ld      c,a               ; store into output

20a8 7e        ld      a,(hl)            ; get RED
20a9 23        inc     hl                ; move to next byte of colour (assuming alpha)
20aa c610      add     a,10h             ; brighten up a bit
20ac 3002      jr      nc,SkipSatR       ; no overflow?
20ae 3eff      ld      a,0ffh            ; Saturate to $FF

SkipSatR
20b0 e6e0      and     0e0h              ; keep top 3 bits
20b2 b1        or      c                 ; merge with output pixel
20b3 12        ld      (de),a            ; store converted pixel
20b4 13        inc     de                ; move to next ouput pixel
20b5 23        inc     hl                ; move to next BGRA pixel
20b6 10d0      djnz    ConvertionLoop    ; c = pixel....?!?!?!

20b8 06c0      ld      b,0c0h            ; bc=$c000

ConvertUploadLoop:
20ba c5        push    bc
20bb dd21003e  ld      ix,3e00h          ; $3e00 = Destination address
20bf 010001    ld      bc,0100h          ; read 256 bytes of data
20c2 cd5220    call    ReadBytes         ; Read from file

;
; convert 256 value palette index into actual RGB byte pixel using palette lookup
;
20c5 2e00      ld      l,00h             ; l = xx (loop counter)

CopyLoop:
20c7 263e      ld      h,3eh             ; hl = $3exx
20c9 5e        ld      e,(hl)            ; e = Get palette index
20ca 163f      ld      d,3fh             ; d = $3f palette base address - 256 byte aligned
20cc 1a        ld      a,(de)            ; a = palette value (24bit converted downto 8bit)
20cd 265b      ld      h,5bh             ; hl = $5bxx converted 256 byte buffer
20cf 77        ld      (hl),a            ; ($5bxx) = converted colour pixel
20d0 263e      ld      h,3eh             ; hl = $3e00
20d2 2c        inc     l                 ; do 256 bytes of this....
20d3 20f2      jr      nz,CopyLoop

20d5 c1        pop     bc                ; bc = $c000
20d6 c5        push    bc
20d7 cd0060    call    6000h             ; block transfer 256 bytes of the bitmap
20da c1        pop     bc
20db 10dd      djnz    ConvertUploadLoop
20dd 013b12    ld      bc,123bh          ; Bitmap port
20e0 3e02      ld      a,02h             ; 2 = enable and visible
20e2 ed79      out     (c),a             ; switch bitmap layer 2 on
20e4 c35a20    jp      205ah

BMPHeader:     ds   54                   ; $20e7 to $211c

PrintText:
211d 7e        ld      a,(hl)            ; get character
211e 23        inc     hl                ; move to next one
211f b7        or      a                 ; is this 0? 
2120 c8        ret     z                 ; if so... end of string
2121 d7        rst     10h               ; outchr()
2122 18f9      jr      211dh             ; print all characters

;
; Looks like data of some kinds
;
FileName: db  "filename.ext",0
Message:  db  ".picload  to load image to background",$d,$00
HL_Save   dw  0

;
; Copied up to $6000
;
UploadCode:
2164 05        dec     b                ; b = upper byte of memory address
2165 78        ld      a,b              ; get 256 byte page into a
2166 e63f      and     3fh              ; ignore top bits
2168 57        ld      d,a              ; de=$0000-$3ffff 
2169 1e00      ld      e,00h            ; Page bitmap block into lower 16K
216b 78        ld      a,b
216c e6c0      and     0c0h             ; get the 16K bank to page
216e f601      or      01h              ; OR in bank active bit
2170 013b12    ld      bc,123bh         ; bitmap register
2173 ed79      out     (c),a            ; map bank into memory perhaps?
2175 21005b    ld      hl,5b00h         ; $5b00 src image chunk
2178 010001    ld      bc,0100h         ; 256 byte copy (one line)
217b edb0      ldir                     ; copy up  (de=dest)
217d 013b12    ld      bc,123bh         ; bitmap register
2180 ed69      out     (c),l            ; l=0, disable current bank and screen
2182 c9        ret     

BitMap:
2183 00        nop                      ; file is loaded into here....