POC VERSION TWO
Re: POC VERSION TWO
Thanks for the in-depth posts BDD, I've started playing with the 28L92 myself and had a lot of questions around the best ways to handle the various interrupt reasons but it looks like you've answered them 
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC VERSION TWO: More Interrupts
LIV2 wrote:
Thanks for the in-depth posts BDD...
Quote:
...I've started playing with the 28L92 myself and had a lot of questions around the best ways to handle the various interrupt reasons but it looks like you've answered them 
Speaking of 'L92 interrupt causes, one of the DUART's features I'm hoping to eventually exploit is its ability to set up pins OP4, OP5, OP6 and OP7 as discrete IRQ outputs, two of them indicating when a receiver interrupts and the other two indicating when a transmitter interrupts. In POC V2.1, the two DUARTs comprising the virtual QUART (vQUART) have their IRQ outputs connected to two inputs on one section three-input AND gate (74AC11) that drives the 65C816's IRQB input. This is a measure I took to avoid a "lazy" IRQ circuit that might cause spurious interrupts to occur. The expansion socket also has a pin wired to the same gate to pick up IRQs from whatever is plugged into the socket (usually the SCSI host adapter).
Unfortunately, this arrangement doesn't indicate from where the interrupt came, only that one occurred. That means the interrupt service routine has to poll all likely sources, which in the case of the vQUART, means at least nine of them: the four receivers, the four transmitters and the counter/timer in DUART A. The SCSI host adapter is another interrupt source, with the interrupt coming from the 53CF94 controller, the DS1511 real-time clock, or both. So potentially 11 sources would have to be checked. While the code I've written to process these IRQs is succinct it does do a fair amount of busy work that could be eliminated with the right hardware.
In a future design that will exploit the DUARTs' discrete IRQ feature, I will wire these outputs to separate inputs on the CPLD, using a resistor network to pull up each input, since the DUART's IRQ outputs are open drain. When a channel interrupts, the CPLD logic will assert IRQB on the MPU, as well as set bits in a read-only status register to indicate which channels are interrupting:
Code: Select all
vDUART CPLD IRQ STATUS
——————————————————————
xxxxxxxx
||||||||
|||||||+———> 1: channel A RxD
||||||+————> 1: channel B RxD
|||||+—————> 1: channel C RxD
||||+——————> 1: channel D RxD
|||+———————> 1: channel A TxD
||+————————> 1: channel B TxD
|+—————————> 1: channel C TxD
+——————————> 1: channel D TxDSo far, I've conjured the following interrupt service routine code framework that utilizes the vQUART status to minimize clock cycle consumption:
Code: Select all
; process vQUART channel interrupts...
;
lda HMUBAS+L92IRQ ;read vQUART status from CPLD...
;
; ———————————————————————————————————————————————————————————————————
; HMUBAS is the base address of the "hardware management unit" (HMU),
; which is a set of registers within the CPLD that is the glue logic
; for the system. L92IRQ is a read-only register that reflects the
; real-time status of the channel IRQ outputs from the 2 DUARTs that
; comprise the virtual QUART (vQUART).
; ———————————————————————————————————————————————————————————————————
;
beq ser_done ;no channels are interrupting...
;
; ———————————————————————————————————————————————————————————————————————
; If the status is zero, no channels are interrupting & hence there is no
; reason to enter the channel servicing part of the interrupt handler &
; waste a bunch of clock cycles.
; ———————————————————————————————————————————————————————————————————————
;
;
; RxD processing loop...
;
rxdloop ldy #NXPNCHAN-1 ;starting channel index...
;
; —————————————————————————————————————————————————
; NXPNCHAN is the number of communications channels
; built into the hardware.
; —————————————————————————————————————————————————
;
.0000010 lsr a ;test channel RxD
bcc .0000020 ;not interrupting
;
pha ;save vQUART status
tya ;current channel index
asl a ;generate corresponding...
tax ;channel offset
;
; ——————————————————————————————————————————————————————————————————————
; At this point, the channel receiver is serviced, using the direct page
; pointer array set up during POST to access the appropriate device reg-
; isters. After the receiver has been serviced the loop code will cont-
; inue as follows.
; ——————————————————————————————————————————————————————————————————————
;
pla ;recover vQUART status
;
.0000020 dey ;all channels checked?
bpl .0000010 ;no, loop
;
; ————————————————————————————————————————————————————————————————————————
; The receivers have been checked & serviced so now it's the transmitters'
; turn to be serviced. All TxD status bits have been shifted into the low
; nybble in the accumulator, which means the same basic loop structure may
; be used to service the transmitters. However, we first determine if any
; transmitters need servicing before entering the loop.
; ————————————————————————————————————————————————————————————————————————
;
txdloop tay ;any transmitters interrupting?
beq ser_done ;no, we're done with serial I/O
;
ldy #NXPNCHAN-1 ;starting channel index
;
.0000010 lsr a ;test channel TxD
bcc .0000020 ;not interrupting
;
pha ;save vQUART status
tya ;current channel index
asl a ;generate corresponding...
tax ;channel offset
;
; ——————————————————————————————————————————————————————————————————————
; At this point, code services the channel transmitter, using the direct
; page pointer array set up during POST to access the appropriate device
; registers. After the transmitter has been serviced the loop code con-
; tinues as follows.
; ——————————————————————————————————————————————————————————————————————
;
pla ;recover vQUART status
;
.0000020 dey ;all channels checked?
bpl .0000010 ;no, loop
;
ser_done ;...serial processing completed —— program continues...x86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
POC VERSION TWO: V2.1 Update
Progress has been slow on POC V2.1 as of late. The SMD stuff was soldered to the PCB back in June, but the unit had to collect dust until now as I again underwent more treatment of my left eye. Vision has slightly improved, enough so that I was able to resume some electronics bench work.
In the above photo, the partially completed unit is being checked for basic power distribution after undergoing an ohmmeter check for egregious faults. This was also a check for proper operation of the reset circuits. Proper voltages appear at all four serial ports, which means the two MAX-238 transceivers are alive and I correctly installed all the tantalum charge pump capacitors.
So far, so good.
Installed active devices in the above photo are the two 512K SRAMs (near bottom right), two MAX-238 transceivers (left), a NAND gate being used as an inverter (near bottom left), the IRQ AND gate (immediately above the left hand DIP socket), the UARTs' X1 clock generator (bottom left) and the two Maxim DS1813 reset generators (right side above MPU socket), one of which is used to dampen the NMI circuit. I see a response when I manually negate each of the IRQ AND gate inputs, as well as the NMI circuit, so I'm good there.
Remaining assembly work is to install all the bypass capacitors, and the 10 pin JTAG port header, which I forgot to order along with the other parts.
In the above photo, the partially completed unit is being checked for basic power distribution after undergoing an ohmmeter check for egregious faults. This was also a check for proper operation of the reset circuits. Proper voltages appear at all four serial ports, which means the two MAX-238 transceivers are alive and I correctly installed all the tantalum charge pump capacitors.
Installed active devices in the above photo are the two 512K SRAMs (near bottom right), two MAX-238 transceivers (left), a NAND gate being used as an inverter (near bottom left), the IRQ AND gate (immediately above the left hand DIP socket), the UARTs' X1 clock generator (bottom left) and the two Maxim DS1813 reset generators (right side above MPU socket), one of which is used to dampen the NMI circuit. I see a response when I manually negate each of the IRQ AND gate inputs, as well as the NMI circuit, so I'm good there.
Remaining assembly work is to install all the bypass capacitors, and the 10 pin JTAG port header, which I forgot to order along with the other parts.
x86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
POC VERSION TWO: V2.1 Update
POC V2.1 is fully assembled and passes all preliminary checks. Remaining are programming the CPLD and writing test code to see if the hardware works. Then will come time to see if she goes or blows. 
Last edited by BigDumbDinosaur on Sat Oct 21, 2017 2:18 pm, edited 1 time in total.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: POC VERSION TWO
This looks great BDD! It also reminded me how many things I borrowed from your design for my own test SBC, even down to the power connector
. Thanks for sharing the details and best of luck bringing it up.
C74-6502 Website: https://c74project.com
Re: POC VERSION TWO
Good to see you're back able to work on it again, BDD.
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC VERSION TWO
whartung wrote:
Good to see you're back able to work on it again, BDD.
x86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
POC VERSION TWO: V2.1 First Test
BigDumbDinosaur wrote:
POC V2.1 is fully assembled and passes all preliminary checks. Remaining are programming the CPLD and writing test code to see if the hardware works. Then will come time to see if she goes or blows. 
The ROM is programmed to act as a NOP generator. At reset, the ROM is mapped in from $00E000-$00FFFFF. So I merely assembled a bunch of $EA bytes from $00E000-$00FFDB. At $00FFDC is JMP $E000. The hardware reset vector points to $E000, causing the 65C816 run a gazilion NOPs, jump back to $E000, and around-and-around we go. Very simple and capable of simultaneously proving several circuit functions.
I placed a logic probe on the /RD (read data) line, ducked under the bench and flipped the switch (actually, I didn't duck—I'm fearless when it comes to five volts).As soon as the MPU came out of reset the logic probe started showing activity, which meant /RD was being pulsed. The logic for /RD is RWB && Ø2, so that meant the MPU had to be reading instructions from somewhere.A check of pin 20 on the ROM, which is /CE, showed activity. The /CEs of the DUARTs and SRAMS were all high, as were the expansion socket's I/O select pins. Based upon that, my conclusion is the core CPLD device select logic is working and instructions are coming from ROM.
I also checked a number of other control signals and could see what appeared to be proper activity, especially /RDY, which should be cycling due to ROM accesses being wait-stated.
Given what I've seen so far, it appears the POC V2.1 is functional at a basic level. In other words, she goes and didn't blow.
x86? We ain't got no x86. We don't NEED no stinking x86!
- floobydust
- Posts: 1394
- Joined: 05 Mar 2013
Re: POC VERSION TWO
Glad the stay in the hospital wasn't so long... and nice to see some initial power-up progress on POC V2. I've not yet ventured into the 65C816 yet, but it's on my list once I get a couple other 65xx projects completed. Looking forward to seeing the rest of POC V2 coming to life.
Regards, KM
https://github.com/floobydust
https://github.com/floobydust
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC VERSION TWO
floobydust wrote:
Glad the stay in the hospital wasn't so long... and nice to see some initial power-up progress on POC V2. I've not yet ventured into the 65C816 yet, but it's on my list once I get a couple other 65xx projects completed. Looking forward to seeing the rest of POC V2 coming to life.
Before I progress to ROM version 0.1.0, I'm going to create ROM version 0.0.1 to verify some other circuit functions. V0.0.1 will write the ROM's $EA pattern into RAM and then jump to the RAM version. That will run and when the top of "base RAM" (RAM appearing at $000000-$00BFFF) is reached, will jump into ROM to continue executing NOPs. The top of ROM will cause a jump back into RAM, and so forth. Such code will exercise more of the CPLD logic and at least prove that part of RAM is functional.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: POC VERSION TWO
Progress is progress, hope you heal up and feel better soon BD!
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC VERSION TWO
BigDumbDinosaur wrote:
Before I progress to ROM version 0.1.0, I'm going to create ROM version 0.0.1 to verify some other circuit functions...code will exercise more of the CPLD logic and at least prove that part of RAM is functional.
Code: Select all
00147 ;================================================================================
00148 ;
00149 ;POC V2 MACHINE ARCHITECTURE
00150 ;
00151 ; +—————————————————————+ $0FFFFF
00152 ; | |
00153 ; | |
00154 ; | EXRAM (960 KB) |
00155 ; | |
00156 ; | |
00157 ; +———————+—————————————————————+ $010000
00158 ; | | |
00159 ; | HIRAM | HIROM (8 KB) |
00160 ; | | |
00161 ; +———————+—————————————————————+ $00E000
00162 ; | HMUBAS (0.25 KB) |
00163 ; +—————————————————————+ $00DF00
00164 ; | |
00165 ; | D8RAM (1.75 KB) |
00166 ; | |
00167 ; +———————+—————————————————————+ $00D800
00168 ; | | |
00169 ; | IORAM | I/O hardware (2 KB) |
00170 ; | | |
00171 ; +———————+—————————————————————+ $00D000
00172 ; | | |
00173 ; | LOROM | LORAM (4 KB) |
00174 ; | | |
00175 ; +———————+—————————————————————+ $00C000
00176 ; | |
00177 ; | BASRAM (48 KB) |
00178 ; | |
00179 ; +—————————————————————+ $000000
00180 ;
00181 0000 basram =$000000 ;base RAM
00182 C000 loram =$00c000 ;low RAM, aka C-RAM
00183 D000 ioblk =$00d000 ;I/O hardware
00184 D800 d8ram =$00d800 ;"protected" RAM
00185 DF00 hmubas =$00df00 ;hardware management
00186 E000 hirom =$00e000 ;high ROM, aka E-ROM
00187 0000 exram =$010000 ;extended RAM
00188 ;
00189 ;
00190 ; overlays, selectable thru HMU...
00191 ;
00192 C000 lorom =loram ;low ROM ($00C000)
00193 D000 ioram =ioblk ;I/O RAM ($00D000)
00194 E000 hiram =hirom ;high RAM ($00E000)
00195 ;
00196 ;
00197 ; memory type definitions...
00198 ;
00199 0000 zeropag =$000000 ;physical direct page
00200 0100 absolut =$000100 ;absolute bank $00 RAM
00201 ;
00202 ;
00203 ; memory map segment sizes...
00204 ;
00205 0000 s_bank =exram-zeropag ;bank
00206 C000 s_basram =loram-basram ;base RAM
00207 0700 s_d8ram =hmubas-d8ram ;D8RAM
00208 2000 s_hirom =exram-hirom ;high ROM
00209 0100 s_hmubas =hirom-hmubas ;hardware management
00210 0800 s_ioblk =d8ram-ioblk ;I/O block
00211 1000 s_loram =ioblk-loram ;low RAM
00212 0800 s_ioram =s_ioblk ;I/O RAM
00213 2000 s_hiram =s_hirom ;high RAM
00214 ;
00215 ;
00216 ; I/O device mapping...
00217 ;
00218 D000 io_a =ioblk+$0000 ;device A select
00219 D100 io_b =ioblk+$0100 ;device B select
00220 D200 io_c =ioblk+$0200 ;device C select
00221 D300 io_d =ioblk+$0300 ;device D select
00222 D400 io_e =ioblk+$0400 ;device E select
00223 D500 io_f =ioblk+$0500 ;device F select
00224 D600 io_g =ioblk+$0600 ;device G select
00225 D700 io_h =ioblk+$0700 ;device H select
00226 ;
00227 ;
00228 ; 65C816 hardware vectors...
00229 ;
00230 FFE0 nm_mpuv =$00ffe0 ;'816 mode base address
00231 FFF0 em_mpuv =$00fff0 ;'C02 mode base address
00232 FFE4 nm_copv =$00ffe4 ;COP, '816 mode
00233 FFE6 nm_brkv =$00ffe6 ;BRK, '816 mode
00234 FFE8 nm_abtv =$00ffe8 ;abort, '816 mode
00235 FFEA nm_nmiv =$00ffea ;NMI, '816 mode
00236 FFEE nm_irqv =$00ffee ;IRQ, '816 mode
00237 FFF6 em_brkv =$00fff6 ;BRK, 'C02 mode
00238 FFF8 em_abtv =$00fff8 ;abort, 'C02 mode
00239 FFFA em_nmiv =$00fffa ;NMI, 'C02 mode
00240 FFFC em_rstv =$00fffc ;reset
00241 FFFE em_irqv =$00fffe ;IRQ, 'C02 mode
00242 ;
00243 ;
00244 ; miscellaneous addresses...
00245 ;
00246 FFF0 bankchk =em_mpuv ;...
00247 ;
00248 ; ————————————————————————————————————————————————————————————————————————
00249 ; During the extended RAM bank enumeration test, a null will be written to
00250 ; the BANKCHK location, after which it will be read back. If the bank ex-
00251 ; ists a null will be returned. Otherwise, the contents of BANKCHK, which
00252 ; is located in the MPU vector area of HIROM will be returned. By design,
00253 ; reading BANKCHK in bank $00 will return $FFFF.
00254 ; ————————————————————————————————————————————————————————————————————————
00255 ;
00256 0100 basramck =absolut ;start of base RAM testing
00257 ;
00258 .end
00259 0003 s_jmpabs =3 ;size of JMP ABS
00260 00EA mp_nop =$ea ;NOP instruction opcode
00261 CFFD stophere =ioblk-s_jmpabs ;end of NOP test space
00262 ;
00263 ;================================================================================
00264 ;
00265 ;MAIN TEST CODE
00266 ;
00267 E000 *=hirom
00268 ;
00269 E000 A9 EA lda #mp_nop
00270 E002 A2 00 ldx #<absolut
00271 E004 A0 01 ldy #>absolut
00272 ;
00273 E006 86 00 main stx zeropag
00274 E008 84 01 sty zeropag+1
00275 E00A 92 00 sta (zeropag)
00276 E00C E8 inx
00277 E00D D0 01 bne .0000010
00278 ;
00279 E00F C8 iny
00280 ;
00281 E010 C0 CF .0000010 cpy #>stophere
00282 E012 90 F2 bcc main
00283 ;
00284 E014 E0 FD cpx #<stophere
00285 E016 90 EE bcc main
00286 ;
00287 E018 A2 02 ldx #s_jmpabs-1
00288 ;
00289 E01A BD 23 E0 .0000020 lda jmpabs,x
00290 E01D 9D FD CF sta stophere,x
00291 E020 CA dex
00292 E021 10 F7 bpl .0000020
00293 ;
00294 E023 4C 00 01 jmpabs jmp absolutx86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
POC VERSION TWO: Firmware Development
Further testing has determined that I can read and write both of POC V2.1's DUARTs, which means I should be able to produce a display on the console screen and read the console keyboard. It was relatively easy to take the interrupt part of POC V1.1's serial I/O (SIO) driver and adapt it to V2. Some minor rework was required to adapt the foreground part of the driver. Accordingly, I'm working up some firmware that I hope to have ready to go in a few days. While doing so, I have a bit of a conundrum to resolve.
V1 has two SIO channels, A and B, indexed as 0 and 1—channel 0 is the console. There are four BIOS API calls for doing SIO: read from channel A, write to channel A, read from channel B and write to channel B. That arrangement has worked well and in fact, came about because all of the firmware, which includes the machine language monitor, as well as reset, interrupt code and the BIOS, has to fit within 8 KB of ROM. In contrast, V2 has 8 KB of ROM devoted to the BIOS, interrupt handlers and reset code, and a separate 4 KB ROM segment into which the M/L monitor will be loaded. So I have more elbow room for BIOS API refinements, among which could be the consolidation of the SIO functions into two calls, using channel indexing to select the desired input or output path.
V2 has four SIO channels, 0, 1, 2 and 3, hence there are theoretically eight separate API functions for serial I/O, four read and four write. Internally, the code that does SIO is common to all channels—internal indexing selects direct page pointers that select the correct data structures and hardware registers. The indirect indexing principle that selects a channel has been tested on POC V1.1, so I know it works. The question will be how to set the channel index when an SIO API call is made.
Before going any further, the general form of an API call with V2 is:
where <API_NUM> is a 1-based index that ultimately selects the code to be run. Registers are loaded with parameters as required for the call. Returns likewise are passed in the registers, and carry is used to signal "success" or "failure."
Internally, the code that does the SIO processing works with a table that points at the data structures and hardware registers associated with each channel. So I started thinking about a different way of accepting SIO calls that might be more convenient for application programming and came up with several ways in which I could set up a call to, say, write a datum to serial channel C:
The API index would have to be different for each channel. That could be a problem within, say, a general purpose character string printing function. How do you tell it which channel should get the output? The API number corresponding to the desired channel could be pushed via a register with each call, but then that register gets clobbered.
Another method would be:
Here a single API is used for all channels and the channel index is passed as a parameter to the call, improving generality. However, .X would have to be preserved by the caller if its value is important and if it is being used in a loop that is calling the API, would have to be loaded with the channel index at each call.
Still another method would be:
The only difference between this one and the previous method is how the channel index is passed to the API.
I pondered this for some time and then thought about how I/O was done in eight bit Commodore computers. A single "kernal" call, CHROUT, is used to write to anything and a complementary call, CHRIN, is used to read from anything. In order to tell the kernel from where to get input or where to send output, the CHKIN (input) and CHKOUT (output) calls are used. For example, once CHKOUT has been called and has returned error-free, output through CHROUT will "magically" go to the selected device or file. Typical code for doing so would be as follows:
When done, a call to CLRCHN will break the connections set up with CHKIN and CHKOUT, restoring I/O to the screen and keyboard. Obviously, this arrangement produces a degree of generality: a general purpose output subroutine can print anywhere. All the caller has to do is say where to print.
In V2, something similar could be arranged:
The default SIO channel is channel A (0), which is the console. So it's convenient to have a quick call to re-select the default. A similar arrangement to the above would be required to handle input, using the same calling methodology.
SIO API calls read and write circular FIFOs (hardware access is via IRQs), which can happen very fast once the required pointers have been selected. As the channel index doesn't have to be passed with each SIO input or output call, the BIOS primitives code that process the call don't have as much work to do—no channel pointer setup has to be done—and hence should run faster.
There is more complexity in having separate calls to set input and output paths. Also, more direct page storage will be consumed to keep track of which channel is current input and which channel is current output. That said, I'm looking ahead to when a version of POC gets built with eight serial channels. Whatever method I used to select a channel for I/O will have to be sufficiently flexible to handle that many channels. So some planning ahead is probably in order.
V1 has two SIO channels, A and B, indexed as 0 and 1—channel 0 is the console. There are four BIOS API calls for doing SIO: read from channel A, write to channel A, read from channel B and write to channel B. That arrangement has worked well and in fact, came about because all of the firmware, which includes the machine language monitor, as well as reset, interrupt code and the BIOS, has to fit within 8 KB of ROM. In contrast, V2 has 8 KB of ROM devoted to the BIOS, interrupt handlers and reset code, and a separate 4 KB ROM segment into which the M/L monitor will be loaded. So I have more elbow room for BIOS API refinements, among which could be the consolidation of the SIO functions into two calls, using channel indexing to select the desired input or output path.
V2 has four SIO channels, 0, 1, 2 and 3, hence there are theoretically eight separate API functions for serial I/O, four read and four write. Internally, the code that does SIO is common to all channels—internal indexing selects direct page pointers that select the correct data structures and hardware registers. The indirect indexing principle that selects a channel has been tested on POC V1.1, so I know it works. The question will be how to set the channel index when an SIO API call is made.
Before going any further, the general form of an API call with V2 is:
Code: Select all
pea #<api_num> ;push API index
cop $00 ;call API ("trap")Internally, the code that does the SIO processing works with a table that points at the data structures and hardware registers associated with each channel. So I started thinking about a different way of accepting SIO calls that might be more convenient for application programming and came up with several ways in which I could set up a call to, say, write a datum to serial channel C:
Code: Select all
lda #datum ;load datum
pea #_siowrtc_ ;push write channel C API number
cop $00 ;write to channel CAnother method would be:
Code: Select all
lda #datum ;load datum
ldx #siochcix ;channel C's index
pea #_siowrt_ ;push write API number
cop $00 ;write to channel CStill another method would be:
Code: Select all
lda #datum ;load datum
pea #siochcix ;push channel C's index
pea #_siowrt_ ;push write API index
cop $00 ;write to channel CI pondered this for some time and then thought about how I/O was done in eight bit Commodore computers. A single "kernal" call, CHROUT, is used to write to anything and a complementary call, CHRIN, is used to read from anything. In order to tell the kernel from where to get input or where to send output, the CHKIN (input) and CHKOUT (output) calls are used. For example, once CHKOUT has been called and has returned error-free, output through CHROUT will "magically" go to the selected device or file. Typical code for doing so would be as follows:
Code: Select all
;Commodore chkout function call...
;
ldx #5 ;file descriptor
jsr chkout ;set it as output
bcs error
;
lda #'A' ;write 'A'
jsr chrout ;will go to file opened on 5In V2, something similar could be arranged:
Code: Select all
lda #siochcix ;channel C's index
pea #_sioout_ ;set SIO channel API number
cop $00 ;set channel C as output
bcs error ;channel index out of range
;
; At this point, we can endlessly write to channel C, as the BIOS will be
; configured to write to the channel we've selected. The following code
; writes 40 'A's to channel C...
;
lda #'A'
ldx #40
;
loop pea #_siowrt_ ;SIO write API index
cop $00 ;write to 'C' to channel A
dex ;repeat...
bne loop ;39 times
;
;
; restore default SIO channel...
;
pea #_siodef_ ;default SIO channel's API index
cop $00 ;channel A becomes defaultSIO API calls read and write circular FIFOs (hardware access is via IRQs), which can happen very fast once the required pointers have been selected. As the channel index doesn't have to be passed with each SIO input or output call, the BIOS primitives code that process the call don't have as much work to do—no channel pointer setup has to be done—and hence should run faster.
There is more complexity in having separate calls to set input and output paths. Also, more direct page storage will be consumed to keep track of which channel is current input and which channel is current output. That said, I'm looking ahead to when a version of POC gets built with eight serial channels. Whatever method I used to select a channel for I/O will have to be sufficiently flexible to handle that many channels. So some planning ahead is probably in order.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: POC VERSION TWO
Well, I've been fond of the Atari's CIO system, as it's an actual, generic and extensible I/O subsystem with drivers. So, you make the same calls whether you want to read a byte from the keyboard, serial I/O, or disk drive.
But, obviously, it comes at a cost in complexity and RAM, as any more general purpose system would. For example, it has dedicated RAM for 8 channels (0-7) described in I/O Control Blocks of 16 bytes each, so there's 128b of RAM right away. But it's accessed via X and the channel number x 16 . So Channel 1 is $10.
https://www.atarimax.com/freenet/freene ... cle.php?30
More fully detailed in the Technical Reference Notes: http://www.atarimania.com/documents/ata ... -notes.pdf
But, obviously, it comes at a cost in complexity and RAM, as any more general purpose system would. For example, it has dedicated RAM for 8 channels (0-7) described in I/O Control Blocks of 16 bytes each, so there's 128b of RAM right away. But it's accessed via X and the channel number x 16 . So Channel 1 is $10.
https://www.atarimax.com/freenet/freene ... cle.php?30
More fully detailed in the Technical Reference Notes: http://www.atarimania.com/documents/ata ... -notes.pdf
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC VERSION TWO
whartung wrote:
Well, I've been fond of the Atari's CIO system, as it's an actual, generic and extensible I/O subsystem with drivers.
In POC V2.1's case, there are only two serial I/O operations (read and write) that I need to abstract in the BIOS API, so the CIO method would be unnecessarily bulky. I'm kind of leaning toward the Commodore method, as it is simple to implement. What is an unknown right now is if there are performance implications that might favor some other method.
x86? We ain't got no x86. We don't NEED no stinking x86!