I don't know what you mean by "quickly", but here's a "GetBlock" subroutine I've used in the past:

**Code:**

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

;DESCRIPTION:

; Finds the code of the block at the specified map coordinates.

;INPUT:

; BlockX, BlockY: pixel coordinates of the block to find;

;OUTPUT:

; A: code of the requested block;

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

Assembler_StartMember Level, GetBlock

Assembler_StartAbsoluteVariables System::Scratchpad

BlockX: .res 2 ;horizontal coordinate of the block to find

BlockY: .res 2 ;vertical coordinate of the block to find

MapRowAddress: .res 2 ;pointer used to get screens from the level map

Assembler_EndAbsoluteVariables

;read the index of the huge structure from the level map (37 cycles)

lda BlockY+1

and Level::WrapMaskY

tay

lda (Level::MapRowsLow), y

sta MapRowAddress+0

lda (Level::MapRowsHigh), y

sta MapRowAddress+1

lda BlockX+1

and Level::WrapMaskX

tay

lda (MapRowAddress), y

;HugeStructureFound:

;read the index of the large structure from the huge structure (19, 20 or 21 cycles)

tay

bit BlockY+0

bmi :++

bit BlockX+0

bmi :+

lda (Level::HugeStructuresPart0), y

jmp LargeStructureFound

: lda (Level::HugeStructuresPart1), y

jmp LargeStructureFound

: bit BlockX+0

bmi :+

lda (Level::HugeStructuresPart2), y

jmp LargeStructureFound

: lda (Level::HugeStructuresPart3), y

LargeStructureFound:

;read the index of the small structure from the large structure (19, 20 or 21 cycles)

tay

bit BlockY+0

bvc :++

bit BlockX+0

bvc :+

lda (Level::LargeStructuresPart0), y

jmp SmallStructureFound

: lda (Level::LargeStructuresPart1), y

jmp SmallStructureFound

: bit BlockX+0

bvc :+

lda (Level::LargeStructuresPart2), y

jmp SmallStructureFound

: lda (Level::LargeStructuresPart3), y

SmallStructureFound:

;read the index of the tiny structure from the small structure (21, 22 or 23 cycles)

tay

lda #%00100000

bit BlockY+0

bne :++

bit BlockX+0

bne :+

lda (Level::SmallStructuresPart0), y

jmp TinyStructureFound

: lda (Level::SmallStructuresPart1), y

jmp TinyStructureFound

: bit BlockX+0

bne :+

lda (Level::SmallStructuresPart2), y

jmp TinyStructureFound

: lda (Level::SmallStructuresPart3), y

TinyStructureFound:

;read the index of the block from the tiny structure (21, 22 or 23 cycles)

tay

lda #%00010000

bit BlockY+0

bne :++

bit BlockX+0

bne :+

lda (Level::TinyStructuresPart0), y

jmp Return

: lda (Level::TinyStructuresPart1), y

jmp Return

: bit BlockX+0

bne :+

lda (Level::TinyStructuresPart2), y

jmp Return

: lda (Level::TinyStructuresPart3), y

Return:

;return

rts

Assembler_EndMember

I had pointers in ZP pointing to the 4 quadrants of every type of block, and looking at bits from the coordinates I selected which quadrant to read at each step. This subroutine only reads the index of a 16x16-pixel block from the map, if you want more information about the block you still have to look it up.

Now that I'm looking at it again, this does seem to be a bit on the slow side, depending on how many blocks you have to read per frame. I don't see why we can't discuss other possibilities, though. IIRC, this subroutine is meant for random access to anywhere in the map, but when decoding rows and columns of blocks you can probably unroll the code somehow to avoid reading the same blocks over and over (specially the 256x256-pixel block, here called "huge structure"). I never got to do this because at the time I didn't think it was worth the trouble.