The NC100 LCD Display

Because the Z80 is restricted to addressing an area of no more than 64K, if you want any more RAM or ROM you have to page it in to order. This way you can have 16K blocks of memory containg code or data for different purposes and then use a bank switching device to map blocks in when they are needed.

And, ofcourse, this is what the NC100 does. In fact it has 256K ROM and 64K RAM of memory built in so it uses very sophisticated memory management techniques to page everything into the small 64K area at exactly the right times.
Although the screen is only 4K long, that is still too precious an amount of memory to give up permanently, so even the screen RAM is only paged in when it has to be written to or read from (although the LCD display does have permanent access to it, in order to keep it visible all the time).

For technical reasons only 16K chunks of memory can be switched in at any one time, so when you select the screen you get an extra 12K mapped in containing other data. All you need to be concerned about though, is the top 4K area as this is where you will always find the screen.

The RAM containing the screen can be mapped into any of the four 16K locations in the 64K memory map by using the correct OUT statement to ports &10-&13, Like this:

LD	A,67	; This signifies the screen RAM block
OUT	(&10),A	; for &0000, or:
OUT	(&11),A	; for &4000, or:
OUT	(&12),A	; for &8000, or:
OUT	(&13),A	; for &C000

However, it is strongly recommended that BBC Basic programs should use the final one of these calls in order to map the screen in at &C000 so that BBC Basic's own RAM area is not affected. In fact, as it is only 4K long, the screen is mapped in at &F000 and, because the other 12K is reserved, make sure you do sufficient bounds checking so that screen writes don't stray into it.

In addition, the bank switching registers are write-only. Therefor, for the NC100 to know its current status at any time it must refer to its own copy of the various settings. These are held in the copy of the MMU (Memory Management Unit) at locations &B000-&B003. So, before you write to any of the ports you must first read the value from the relevant location and store a copy (perhaps by pushing it on the stack), then write your new value back to this location, and only then write the value to the port, like this:

LD	A,(&B003)	; Load current value
PUSH	AF		; Push it to stack
LD	A,67
LD	(&B003),A	; Write new value to memory
out	(&13),A		; Write new value to bank switcher

To put a screen back from where you got it, pop the value off the stack (or get it from where you stored it) and write it to the location before also writing it to the bank switching port, like this:

POP	AF		; Get old value from stack
LD	(&B003),A	; Write old value to memory
OUT	(&13),A		; Write old value to bank switcher

Courageous users may wish to experiment with using values other than 67 and mapping the NC100's various RAM/ROM blocks somewhere in memory (such as at &4000, so that you can still use the screen) in order to have a peek at how the computer is organised. But this is not recommended for the faint-hearted! You can also examine the RAMs and ROMs using the MM and DU commands available from Protext's command mode.

Anyway, down to the nitty-gritty. Here is some example code for directly accessing the video RAM on a pixel level. In order to access a given X,Y location on the screen you have to perform the following steps:

In the following example HL is the Y pixel. H is always zero and L has a value between 0 and 63, inclusive, while DE is the X offset which ranges from 0.479.
This program will display a single pixel towards the top right-hand side of the display:

10 CLS
20 DIM A% &100
30 PROCassemble
50 END
60 DEF PROCassemble
80 P%=A%
90 [
110 :
120 .start
130 :
140 LD HL,3
150 LD DE,377
160 :
170 ; Save memory, and set video memory
180 :
190 LD A,(&B003)
210 LD A,67
220 LD (&B003),A
230 OUT (&13),A
240 :
250 ; Multiply HL by 64 (bytes per pixel line)
260 :
270 LD H,0
340 :
350 ; Determine which bit to act on
360 :
370 LD A,E
380 AND 7
390 LD B,A
395 INC B ; 
400 LD A,0
410 SCF
420 :
430 .power
440 :
450 RRA
460 DJNZ power
480 :
490 ; Divide DE by 8 to get pixel address
500 :
510 SRL D
520 RR E
530 SRL D
540 RR E
550 SRL D
560 RR E
570 :
580 ; Add on X address to start of pixel line
590 :
610 :
620 ; Convert to range &F000-&FFFF
630 :
640 LD A,H
650 OR &F0
660 LD H,A
670 :
680 ; HL now points at 8 bits of screen memory, so write pixel
690 :
700 POP AF
710 LD B,(HL)
720 OR B
730 LD (HL),A
740 :
750 ; Now clean up
760 :
770 POP AF
780 LD (&B003),A
790 OUT (&13),A
800 RET
810 ]
820 NEXT

Most of this program is pretty self-explanatory, but there are two bits that need further discussion. Take a look at lines 370-470. Here, the E register is copied to A and then ANDed with 7. This leaves it with only the three right-most bits (a number between 0 and 7).
The contents of A are then transferred to B and INCremented, A is zeroed, the carry flag is set, and the loop called power rotates A right the number of times stored in B. This moves the pixel to be set to the correct location. the RRA command moves all the bits in A to the right, at the same time placing the contents of the carry flag in bit 7 (on the left), and the contents of bit 0 into the carry flag.
The value in A is then stored by PUSHing AF on to the stack where it is later retrieved at line 700 and ORed with the contents of the location pointed to by HL. If you wanted to clear the pixel you would first issue an XOR &FF and then AND with B instead.