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 RET
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 40 CLS:CALL A% 50 END 60 DEF PROCassemble 70 FOR PASS=0 TO 3 STEP 3 80 P%=A% 90 [ 100 OPT PASS 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) 200 PUSH AF 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 280 ADD HL,HL 290 ADD HL,HL 300 ADD HL,HL 310 ADD HL,HL 320 ADD HL,HL 330 ADD HL,HL 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 470 PUSH AF 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 : 600 ADD HL,DE 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 830 ENDPROC
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.