1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2025-01-10 06:30:41 +00:00

Get all libraries to build

This commit is contained in:
David Schmenk 2018-01-23 12:20:45 -08:00
parent 1f999a86e8
commit 4a033634ad
16 changed files with 1132 additions and 297 deletions

4
src/inc/fiber.plh Normal file
View File

@ -0,0 +1,4 @@
import fiber
predef fbrInit(numPool), fbrStop(fid)#0, fbrExit#0, fbrStart(defaddr, param)
predef fbrYield#0, fbrHalt#0, fbrResume(fid)#0
end

4
src/inc/portio.plh Normal file
View File

@ -0,0 +1,4 @@
import portio
predef digitalRead(pin), portRead, digitalWrite(pin, val)#0
predef portWrite(val)#0, analogRead(pin), delay(time)#0
end

4
src/inc/sndseq.plh Normal file
View File

@ -0,0 +1,4 @@
import sndseq
predef spkrTone(pitch, duration)#0, spkrPWM(sample, speed, len)#0
predef musicPlay(track, rept)#0, musicStop#0, getKey(backgroundProc)#1
end

View File

@ -1,7 +1,27 @@
import spiport
const SPI_SLAVE_READY = '@'
const SPI_SLAVE_ERROR = '!'
const SPI_SLAVE_BUSY = $FF
//
// Wiring constants for Arduino
//
const PINHIGH = 1
const PINLOW = 0
const PINOUTPUT = 1
const PININPUT = 0
const PINPULLUP = 2
//
// SPI commands to Wiring functions on Arduino
//
const CMDPINMODE = 3
const CMDDIGREAD = 4
const CMDDIGWRITE = 5
const CMDANAREAD = 6
const CMDANAWRITE = 7
//
// SPI commands to serial functions on Arduino
//
const CMDSERMODE = 8
const CMDSERAVAIL = 9
const CMDSERREAD = 10
const CMDSERWRITE = 11
predef spiXferByte(outbyte), spiSend(data), spiRecv, spiWriteBuf(buf, len), spiReadBuf(buf, len)
predef spiDelay(time), spiReady
end

View File

@ -1,23 +0,0 @@
//
// Wiring constants for Arduino
//
const PINHIGH = 1
const PINLOW = 0
const PINOUTPUT = 1
const PININPUT = 0
const PINPULLUP = 2
//
// SPI commands to Wiring functions on Arduino
//
const CMDPINMODE = 3
const CMDDIGREAD = 4
const CMDDIGWRITE = 5
const CMDANAREAD = 6
const CMDANAWRITE = 7
//
// SPI commands to serial functions on Arduino
//
const CMDSERMODE = 8
const CMDSERAVAIL = 9
const CMDSERREAD = 10
const CMDSERWRITE = 11

View File

@ -268,36 +268,3 @@ export def fbrResume(fid)#0
fin
end
done
//
// Test Fiber library
//
def puth(h)#0
word valstr
valstr = "0123456789ABCDEF"
valstr++
putc('$')
putc(valstr->[(h >> 12) & $0F])
putc(valstr->[(h >> 8) & $0F])
putc(valstr->[(h >> 4) & $0F])
putc(valstr->[ h & $0F])
end
def fbrTest(fid, param)#0
byte i
for i = 1 to param
puth(fid); putc($0D)
fbrYield
next
end
//puts("fbrSwap = "); puth(@fbrSwap); putln
fbrInit(4)
fbrStart(@fbrTest, 3)
fbrStart(@fbrTest, 2)
fbrStart(@fbrTest, 1)
fbrYield; fbrYield; fbrYield; fbrYield
done

View File

@ -25,7 +25,7 @@ export def portRead
return (^FLAG0>>7)&1|(^FLAG1>>6)&2|(^FLAG2>>5)&4|(^FLAG3>>4)&8
end
def digitalWrite(pin, val)#0
export def digitalWrite(pin, val)#0
ANN0[((pin&3)<<1)+(val&1)]
end

838
src/libsrc/sndseq.pla Executable file
View File

@ -0,0 +1,838 @@
include "inc/cmdsys.plh"
include "inc/fileio.plh"
include "inc/args.plh"
//
// Usage is documented following the source in this file...
//
const rndseed = $004E
const LSB = 0
const MSB = 1
const MB_ARPEGGIO = 4 // In 16ths of a second
const MAX_MBCH_NOTES = 9
const SPKR_ARPEGGIO = 2 // In 16ths of a second
const DUR16TH = 8
const MAX_SPKR_NOTES = 4
const NOTEDIV = 4
//
// 6522 VIA registers
//
struc t_VIA
byte IORB // I/O Register B
byte IORA // I/O Register A
byte DDRB // Data Direction Register B
byte DDRA // Data Direction Register A
word T1C // Timer 1 Count
word T1L // Timer 1 Latch
word T2C // Timer 2 Count
byte SR // Shift Register
byte ACR // Aux Control Register
byte PCR // Peripheral Control Register
byte IFR // Interrupt Flag Register
byte IER // Interrupt Enable Register
byte IOA_noHS // I/O Register A - no HandShake
end
const T1CH = T1C+1
//
// AY-3-8910 PSG registers
//
struc t_PSG
word AFREQ // A Frequency Period
word BFREQ // B Frequency Period
word CFREQ // C Frequency Period
byte NGFREQ // Noise Generator Frequency Period
byte MIXER // Enable=0/Disable=1 NG C(5) B(4) A(3) Tone C(2) B(1) A(0)
byte AENVAMP // A Envelope/Amplitude
byte BENVAMP // B Envelope/Amplitude
byte CENVAMP // C Envelope/Amplitude
word ENVPERIOD // Envelope Period
byte ENVSHAPE // Envelope Shape
end
//
// Sequence event
//
struc t_event
byte deltatime // Event delta time in 4.4 seconds
byte percnote // Percussion:7==0 ? Pitch:4-0 : Octave:6-4,Note:3-0
byte perchanvol // Percussion ? EnvDur:7-0 : Channel:7,Volume:3-0
end
//
// Predef routines
//
predef musicPlay(track, rept)#0
predef musicStop#0
//
// Static sequencer values
//
word seqTrack, seqEvent, seqTime, eventTime, updateTime, musicSequence
byte numNotes, seqRepeat
byte indexA[2], indexB[2], indexC[2]
byte noteA[2], noteB[2], noteC[2]
word notes1[MAX_MBCH_NOTES], notes2[MAX_MBCH_NOTES]
word notes[2] = @notes1, @notes2
word periods1[MAX_MBCH_NOTES], periods2[MAX_MBCH_NOTES]
word periods[2] = @periods1, @periods2
//
// MockingBoard data.
//
word[] mbVIAs // Treat this as an array of VIA ptrs
word mbVIA1 = -1 // Init to "discover MockingBoard flag" value
word mbVIA2 = 0
//
// Octave basis frequency periods (starting at MIDI note #12)
// Notes will be encoded as basis note (LSNibble) and octave (MSNibble))
//
word[] spkrOctave0 // Overlay and scale mbOctave0 for speaker version
word[12] mbOctave0 = 3900, 3681, 3474, 3279, 3095, 2922, 2758, 2603, 2457, 2319, 2189, 2066
word[5] arpeggioDuration = DUR16TH, DUR16TH, DUR16TH/2, DUR16TH/3, DUR16TH/4
//
// Emulators are broken - they only activate the MockingBoard's 6522 Timer1
// functionality when interrupts are enabled. This music sequencer is run
// in polling mode without the use of MockingBoard interrupts. To work around
// the emulators, MockingBoard interrupts are enabled, but the 6502 IRQs are
// disabled. NO INTERRUPTS ARE HANDLED WHEN PLAYING MUSIC! The previous state
// is restored between playing sequences.
//
asm vmincs
!SOURCE "vmsrc/plvmzp.inc"
end
asm getStatusReg#1
PHP
PLA
DEX
STA ESTKL,X
LDA #$00
STA ESTKH,X
RTS
end
asm setStatusReg(stat)#0
LDA ESTKL,X
INX
PHA
PLP
RTS
end
asm disableInts#0
SEI
RTS
end
asm enableInts#0
CLI
RTS
end
//
// Write Programmable Sound Generator Registers
//
asm psgWriteTone(pVIA, reg, freq, vol)#0
LDA ESTKL+3,X
STA TMPL
LDA ESTKH+3,X
STA TMPH
LDY #$01
LDA ESTKL+2,X
LSR
ADC #$08
STA (TMP),Y
DEY
LDA #$07
STA (TMP),Y
LDA #$04
STA (TMP),Y
LDA ESTKL,X
INY
STA (TMP),Y
DEY
LDA #$06
STA (TMP),Y
LDA #$04
STA (TMP),Y
INX
BNE +
end
asm psgWriteWord(pVIA, reg, val)#0
LDA ESTKL+2,X
STA TMPL
LDA ESTKH+2,X
STA TMPH
+ LDY #$01
TYA
CLC
ADC ESTKL+1,X
STA (TMP),Y
DEY
LDA #$07
STA (TMP),Y
LDA #$04
STA (TMP),Y
LDA ESTKH,X
INY
STA (TMP),Y
DEY
LDA #$06
STA (TMP),Y
LDA #$04
STA (TMP),Y
BNE +
end
asm psgWrite(pVIA, reg, val)#0
LDA ESTKL+2,X
STA TMPL
LDA ESTKH+2,X
STA TMPH
+ LDY #$01
LDA ESTKL+1,X
STA (TMP),Y
DEY
LDA #$07
STA (TMP),Y
LDA #$04
STA (TMP),Y
LDA ESTKL,X
INY
STA (TMP),Y
DEY
LDA #$06
STA (TMP),Y
LDA #$04
STA (TMP),Y
INX
INX
INX
RTS
end
//
// Apple II speaker tone generator routines
//
export asm spkrTone(pitch, duration)#0
STX ESP
LDY ESTKH,X
LDA ESTKL,X
BEQ +
INY
+ STA DSTL
STY DSTH
LDY ESTKH+1,X
LDA ESTKL+1,X
BEQ +
INY
+ STA TMPL
STY TMPH
TAX
LDA #$FF
PHP
SEI
;
; Total loop count is 32 cycles, regardless of path taken
;
- NOP ; 2
NOP ; 2
BCS + ; 3
;---
;+7 = 12 (from BCS below)
+
-- SEC ; 2
DEX ; 2
BNE ++ ; 2/3
;----
; 6/7
DEY ; 2
BNE +++ ; 2/3
;----
;+4/5 = 10/11
BIT $C030 ; 4
LDX TMPL ; 3
LDY TMPH ; 3
;---
;+10 = 20
TONELP SBC #$01 ; 2
BCS - ; 2/3
;----
; 4/5
DEC DSTL ; 5
BNE -- ; 3
;----
;+8 = 12
DEC DSTH ; This sequence isn't accounted for
BNE -- ; since it is taken only in extreme cases
BEQ TONEXIT
++ NOP ; 2
NOP ; 2
;---
;+4 = 11 (from BNE above)
+++ BIT $C000 ; 4
BMI TONEXIT ; 2
BPL TONELP ; 3
;---
;+9 = 20
TONEXIT PLP
LDX ESP
INX
INX
RTS
end
export asm spkrPWM(sample, speed, len)#0
STX ESP
LDY ESTKH,X
LDA ESTKL,X
BEQ +
INY
+ STY DSTH
STA DSTL
LDA ESTKL+2,X
STA SRCL
LDA ESTKH+2,X
STA SRCH
LDY ESTKL+1,X
INY
STY TMPL
LDY #$00
PHP
SEI
- LDA (SRC),Y
SEC
-- LDX TMPL
--- DEX
BNE ---
SBC #$01
BCS --
BIT $C030
INY
BNE +
INC SRCH
+ DEC DSTL
BNE -
DEC DSTH
BNE -
PLP
LDX ESP
INX
INX
INX
RTS
end
//
// Search slots for MockingBoard
//
def mbTicklePSG(pVIA)
pVIA->IER = $7F // Mask all interrupts
pVIA->ACR = $00 // Stop T1 countdown
pVIA->DDRB = $FF // Output enable port A and B
pVIA->DDRA = $FF
pVIA->IORA = $00 // Reset MockingBoard
if pVIA->IORA == $00
pVIA->IORA = $04 // Inactive MockingBoard control lines
if pVIA->IORA == $04
//
// At least we know we have some sort of R/W in the ROM
// address space. Most likely a MockingBoard or John Bell
// 6522 board. We will assume its a MockingBoard because
// emulators fail the following PSG read test.
//
//psgWriteWord(pVIA, 2, $DA7E)
//if mbReadP(pVIA, 2) == $7E and mbReadP(pVIA, 3) == $0A
return pVIA
//fin
fin
fin
return 0
end
def mbSearch(slot)
if slot
mbVIA1 = mbTicklePSG($C000 + (slot << 8))
if mbVIA1
mbVIA2 = mbTicklePSG(mbVIA1 + $80)
return slot
fin
else
for slot = 1 to 7
if slot == 3 or slot == 6
continue
fin
mbVIA1 = mbTicklePSG($C000 + (slot << 8))
if mbVIA1
mbVIA2 = mbTicklePSG(mbVIA1 + $80)
return slot
fin
next
fin
return 0
end
def psgSetup(pVIA)#0
psgWrite(pVIA, MIXER, $3F) // Turn everything off
psgWrite(pVIA, AENVAMP, $00)
psgWrite(pVIA, BENVAMP, $00)
psgWrite(pVIA, CENVAMP, $10)
psgWrite(pVIA, NGFREQ, $01)
psgWriteWord(pVIA, ENVPERIOD, $0001)
psgWrite(pVIA, ENVSHAPE, $00) // Single decay
psgWriteWord(pVIA, AFREQ, $0000) // Fast response to update
psgWriteWord(pVIA, BFREQ, $0000)
psgWriteWord(pVIA, CFREQ, $0000)
psgWrite(pVIA, MIXER, $38) // Tone on C, B, A
end
//
// Sequence notes through MockingBoard
//
def mbSequence(yield, func)#0
word period, n, yieldTime
byte note, volume, channel, i, overflow, status, quit
//
// Reset oscillator table
//
indexA[0] = 0; indexA[1] = 0
indexB[0] = 1; indexB[1] = 1
indexC[0] = 2; indexC[1] = 2
noteA[0] = 0; noteA[1] = 0
noteB[0] = 0; noteB[1] = 0
noteC[0] = 0; noteC[1] = 0
//
// Get the PSGs ready
//
status = getStatusReg
disableInts
mbVIA1->ACR = $40 // Continuous T1 interrupts
mbVIA1=>T1L = $F9C2 // 16 Ints/sec
mbVIA1=>T1C = $F9C2 // 16 Ints/sec
mbVIA1->IFR = $40 // Clear interrupt
mbVIA1->IER = $C0 // Enable Timer1 interrupt
psgSetup(mbVIA1)
if mbVIA2; psgSetup(mbVIA2); fin
overflow = 0
if yield and func
yieldTime = seqTime + yield
else
yieldTime = $7FFF
fin
updateTime = seqTime
quit = FALSE
repeat
while eventTime == seqTime
note = seqEvent->percnote
if note & $80
//
// Note event
//
volume = seqEvent->perchanvol
channel = (volume & mbVIA2.LSB) >> 7 // Clever - mbVIA2.0 will be $80 if it exists
if volume & $0F
//
// Note on
//
for i = 0 to MAX_MBCH_NOTES-1
//
// Look for available slot in active note table
//
if !notes[channel, i].LSB //or notes[channel, i] == note
break
fin
next
//
// Full note table, kick one out
//
if i == MAX_MBCH_NOTES
i = overflow
overflow = (overflow + 1) % MAX_MBCH_NOTES
else
numNotes++
fin
notes[channel, i] = note | (volume << 8)
periods[channel, i] = mbOctave0[note & $0F] >> ((note >> 4) & $07)
else
//
// Note off
//
for i = 0 to MAX_MBCH_NOTES-1
//
// Remove from active note table
//
if notes[channel, i].LSB == note
notes[channel, i] = 0
numNotes--
break
fin
next
fin
updateTime = seqTime
else
//
// Percussion event
//
period = seqEvent->perchanvol
if period
if (period & $80)
psgWrite(mbVIA1, MIXER, $1C) // NG on C, Tone on B, A
psgWrite(mbVIA1, CENVAMP, $10)
psgWrite(mbVIA1, ENVSHAPE, (note >> 4) & $04)
psgWrite(mbVIA1, NGFREQ, (note >> 1) & $1F)
psgWrite(mbVIA1, ENVPERIOD+1, period & $7F)
elsif mbVIA2
psgWrite(mbVIA2, MIXER, $1C) // NG on C, Tone on B, A
psgWrite(mbVIA2, CENVAMP, $10)
psgWrite(mbVIA2, ENVSHAPE, (note >> 4) & $04)
psgWrite(mbVIA2, NGFREQ, (note >> 1) & $1F)
psgWrite(mbVIA2, ENVPERIOD+1, period)
fin
else
if seqRepeat
//
// Reset sequence
//
musicPlay(seqTrack, TRUE)
seqTime = -1 // Offset seqTime++ later
else
musicStop
fin
quit = TRUE // Exit out
break
fin
fin
//
// Next event
//
seqEvent = seqEvent + t_event
eventTime = seqEvent->deltatime + eventTime
loop
if updateTime <= seqTime
//
// Time slice active note tables (arpeggio)
//
for channel = 0 to 1
//
// Multiplex oscillator A
//
i = indexA[channel]
repeat
i = (i + 3) % MAX_MBCH_NOTES
n = notes[channel, i]
if n // Non-zero volume
break
fin
until i == indexA[channel]
if n.LSB <> noteA[channel]
psgWriteTone(mbVIAs[channel], AFREQ, periods[channel, i], n.MSB)
noteA[channel] = n.LSB
indexA[channel] = i
fin
//
// Multiplex oscillator B
//
i = indexB[channel]
repeat
i = (i + 3) % MAX_MBCH_NOTES
n = notes[channel, i]
if n // Non-zero volume
break
fin
until i == indexB[channel]
if n.LSB <> noteB[channel]
psgWriteTone(mbVIAs[channel], BFREQ, periods[channel, i], n.MSB)
noteB[channel] = n.LSB
indexB[channel] = i
fin
//
// Multiplex oscillator C
//
i = indexC[channel]
repeat
i = (i + 3) % MAX_MBCH_NOTES
n = notes[channel, i]
if n // Non-zero volume
break
fin
until i == indexC[channel]
if n.LSB <> noteC[channel]
psgWrite(mbVIAs[channel], MIXER, $38) // Tone on C, B, A
psgWriteTone(mbVIAs[channel], CFREQ, periods[channel, i], n.MSB)
noteC[channel] = n.LSB
indexC[channel] = i
fin
next
updateTime = seqTime + MB_ARPEGGIO - (numNotes >> 2)
fin
//
// Increment time tick
//
seqTime++
while !(mbVIA1->IFR & $40) // Wait for T1 interrupt
if ^$C000 > 127; quit = TRUE; break; fin
*rndseed++
loop
mbVIA1->IFR = $40 // Clear interrupt
if yieldTime <= seqTime; func()#0; yieldTime = seqTime + yield; fin
until quit
psgWrite(mbVIA1, MIXER, $FF) // Turn everything off
psgWrite(mbVIA1, AENVAMP, $00)
psgWrite(mbVIA1, BENVAMP, $00)
psgWrite(mbVIA1, CENVAMP, $00)
if mbVIA2
psgWrite(mbVIA2, MIXER, $FF)
psgWrite(mbVIA2, AENVAMP, $00)
psgWrite(mbVIA2, BENVAMP, $00)
psgWrite(mbVIA2, CENVAMP, $00)
fin
mbVIA1->ACR = $00 // Stop T1 countdown
mbVIA1->IER = $7F // Mask all interrupts
mbVIA1->IFR = $40 // Clear interrupt
setStatusReg(status))
end
//
// Sequence notes through Apple II speaker
//
def spkrSequence(yield, func)#0
word period, duration, yieldTime
byte note, i, n, overflow
//
// Start sequencing
//
overflow = 0
if yield and func
yieldTime = seqTime + yield
else
yieldTime = $7FFF
fin
updateTime = seqTime
repeat
while eventTime == seqTime
note = seqEvent->percnote
if note & $80
//
// Note event
//
if seqEvent->perchanvol & $0F
//
// Note on
//
for i = 0 to MAX_SPKR_NOTES-1
//
// Look for available slot in active note table
//
if !notes1[i] or note == notes1[i]
break
fin
next
if i == MAX_SPKR_NOTES
//
// Full note table, kick one out
//
overflow = (overflow + 1) & (MAX_SPKR_NOTES-1)
i = overflow
elsif !notes1[i]
//
// Add new note
//
numNotes++
fin
notes1[i] = note
periods1[i] = spkrOctave0[note & $0F] >> ((note >> 4) & $07)
else
//
// Note off
//
for i = 0 to MAX_SPKR_NOTES-1
//
// Remove from active note table
//
if notes1[i] == note
notes1[i] = 0
numNotes--
break
fin
next
fin
else
//
// Percussion event
//
if seqEvent->perchanvol
//spkrPWM($D000, 0, 64) // Play some random sample as percussion
else
if seqRepeat
musicPlay(seqTrack, TRUE)
else
musicStop
fin
return
fin
fin
//
// Next event
//
seqEvent = seqEvent + t_event
eventTime = eventTime + seqEvent->deltatime
loop
if numNotes > 1
for i = 0 to MAX_SPKR_NOTES-1
if notes1[i]
spkrTone(periods1[i], arpeggioDuration[numNotes])
fin
*rndseed++
next
seqTime++
else
period = 0
for i = 0 to MAX_SPKR_NOTES-1
if notes1[i]
period = periods1[i]
break;
fin
*rndseed++
next
duration = eventTime - seqTime
seqTime = duration + seqTime
spkrTone(period, DUR16TH * duration)
fin
if ^$C000 > 127; return; fin
if yieldTime <= seqTime; func()#0; yieldTime = seqTime + yield; fin
until FALSE
end
//
// No sequence, just waste time and yield
//
def noSequence(yield, func)#0
//
// Start wasting time
//
if !yield or !func
yield = 0
fin
seqTime = 0
repeat
seqTime++
if seqTime < 0; seqTime = 1; fin // Capture wrap-around
*rndseed++
spkrTone(0, DUR16TH) // Waste 16th of a second playing silence
if ^$C000 > 127; return; fin
if yield == seqTime; func()#0; seqTime = 0; fin
until FALSE
end
//
// Start sequencing music track
//
export def musicPlay(track, rept)#0
byte i
//
// First time search for MockingBoard
//
if mbVIA1 == -1
if !mbSearch(0)
//
// No MockingBoard - scale octave0 for speaker
//
for i = 0 to 11
spkrOctave0[i] = mbOctave0[i]/NOTEDIV
next
fin
fin
//
// Zero out active notes
//
for i = 0 to MAX_MBCH_NOTES-1; notes1[i] = 0; notes2[i] = 0; next
for i = 0 to MAX_MBCH_NOTES-1; periods1[i] = 0; periods2[i] = 0; next
//
// Start sequencing
//
seqRepeat = rept
seqTrack = track
seqEvent = seqTrack
seqTime = 0
eventTime = seqEvent->deltatime
numNotes = 0
//
// Select proper sequencer based on hardware
//
if mbVIA1
musicSequence = @mbSequence
else
musicSequence = @spkrSequence
fin
end
//
// Stop sequencing music track
//
export def musicStop#0
musicSequence = @noSequence
end
//
// Get a keystroke and convert it to upper case
//
export def getKey(backgroundProc)#1
byte key
while ^$C000 < 128
musicSequence($08, backgroundProc)#0 // Call background proc every half second
loop
key = ^$C000 & $7F
^$C010
return key
end
done
////////////////////////////////////////////////////////////////////////////////
There are three main externally callable routines in this module:
musicPlay(trackPtr, trackRepeat)
Start playing a track sequence in the getUpperKey routine
Params:
Pointer to a track sequence created from the cvtmidi.py tool
Repeat flag - TRUE or FALSE.
The first time its is called, it will try and search for a MockingBoard.
However, it is noted that this can cause problems if a Z-80 card is installed.
The scanning routine might cause a hang if it encounters a Z-80 card before
it finds a MockingBoard. In order to make this robust, it might be best to
prompt the user to search for the MockingBoard, enter the actual MockingBoard
slot, or skip the MockingBoard and use the internal speaker.
musicStop()
Stop playing a track sequence in the getUpperKey routine
The getUpperKey routine will call a dummy sequence routine that will
keep the correct timing for any background processing
getKey()
Wait for a keypress and return the character
While waiting for the keypress, the track sequence will be played though
either the MockingBoard (if present) or the internal speaker. Optionally,
a background function can be called periodically based on the sequencer
timing, so its pretty accurate.
The low level internal speaker routines used to generate tones and waveforms
can be called for warnings, sound effects, etc:
spkrTone(period, duration)
Play a tone
Params:
(1020000 / 64 / period) Hz
(duration * 32 * 256 / 1020000) seconds
spkrPWM(samples, speed, len)
Play a Pulse Width Modulated waveform
Params:
Pointer to 8 bit pulse width samples
Speed to play through samples
Length of sample
The main routines for sequencing music are:
mbSequence(yield, func)
spkrSequence(yield, func)
noSequence(yield, func)
All three try and provide more functionality than would be present in
previous music sequencers. The MockingBoard sequencer will attempt to play up
to 9 tones per sound generator (18 if a MockingBoard II is found). Up to
four notes will be played simultaneously on the internal speaker. In order
to play more notes than the hardware normally supports, a technique using
arpeggio (playing multiple notes in a quick sequence rather than concurrently)
pulls off this feat. The sequencers will immediately return if a keypress is
detected. Finally, during the sequencing, a background function can be periodically
called every 'yield' time which has a resolution of a 16th of a second. Pass
in zero for 'yield' and/or 'func' to disable any background calls.

View File

@ -23,6 +23,8 @@ CONIO = CONIO\#FE1000
SANE = SANE\#FE1000
FPSTR = FPSTR\#FE1000
FPU = FPU\#FE1000
SNDSEQ = SNDSEQ\#FE1000
PLAYSEQ = PLAYSEQ\#FE1000
SANITY = SANITY\#FE1000
RPNCALC = RPNCALC\#FE1000
WIZNET = WIZNET\#FE1000
@ -49,6 +51,7 @@ TESTLIB = TESTLIB\#FE1000
PROFILE = PROFILE\#FE1000
MEMMGR = MEMMGR\#FE1000
MEMTEST = MEMTEST\#FE1000
FIBERTEST = FIBERTEST\#FE1000
FIBER = FIBER\#FE1000
LONGJMP = LONGJMP\#FE1000
PLASM = plasm
@ -72,7 +75,7 @@ TXTTYPE = .TXT
#SYSTYPE = \#FF2000
#TXTTYPE = \#040000
all: $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM802) $(PLVM03) $(CMD) $(PLASMAPLASM) $(CODEOPT) $(ARGS) $(MEMMGR) $(MEMTEST) $(FIBER) $(LONGJMP) $(ED) $(MON) $(ROD) $(SIEVE) $(UTHERNET2) $(UTHERNET) $(ETHERIP) $(INET) $(DHCP) $(HTTPD) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(ROGUEIO) $(HGR1) $(TONE) $(DGR) $(DGRTEST) $(FILEIO) $(CONIO) $(PORTIO) $(SPIPORT) $(SDFAT) $(FATCAT) $(FATGET) $(FATPUT) $(FATWDSK) $(FATRDSK) $(SANE) $(FPSTR) $(FPU) $(SANITY) $(RPNCALC)
all: $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM802) $(PLVM03) $(CMD) $(PLASMAPLASM) $(CODEOPT) $(ARGS) $(MEMMGR) $(MEMTEST) $(FIBER) $(FIBERTEST) $(LONGJMP) $(ED) $(MON) $(ROD) $(SIEVE) $(UTHERNET2) $(UTHERNET) $(ETHERIP) $(INET) $(DHCP) $(HTTPD) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(ROGUEIO) $(HGR1) $(TONE) $(DGR) $(DGRTEST) $(FILEIO) $(CONIO) $(PORTIO) $(SPIPORT) $(SDFAT) $(FATCAT) $(FATGET) $(FATPUT) $(FATWDSK) $(FATRDSK) $(SANE) $(FPSTR) $(FPU) $(SANITY) $(RPNCALC) $(SNDSEQ) $(PLAYSEQ)
clean:
-rm *FE1000 *FF2000 $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM03)
@ -156,6 +159,18 @@ $(FIBER): libsrc/fiber.pla $(PLVM02) $(PLASM)
./$(PLASM) -AMOW < libsrc/fiber.pla > libsrc/fiber.a
acme --setpc 4094 -o $(FIBER) libsrc/fiber.a
$(FIBERTEST): samplesrc/fibertest.pla $(PLVM02) $(PLASM)
./$(PLASM) -AMOW < samplesrc/fibertest.pla > samplesrc/fibertest.a
acme --setpc 4094 -o $(FIBERTEST) samplesrc/fibertest.a
$(SNDSEQ): libsrc/sndseq.pla $(PLVM02) $(PLASM)
./$(PLASM) -AMOW < libsrc/sndseq.pla > libsrc/sndseq.a
acme --setpc 4094 -o $(SNDSEQ) libsrc/sndseq.a
$(PLAYSEQ): samplesrc/playseq.pla $(PLVM02) $(PLASM)
./$(PLASM) -AMOW < samplesrc/playseq.pla > samplesrc/playseq.a
acme --setpc 4094 -o $(PLAYSEQ) samplesrc/playseq.a
$(LONGJMP): libsrc/longjmp.pla $(PLVM02) $(PLASM)
./$(PLASM) -AMOW < libsrc/longjmp.pla > libsrc/longjmp.a
acme --setpc 4094 -o $(LONGJMP) libsrc/longjmp.a

88
src/mkrel Executable file
View File

@ -0,0 +1,88 @@
cp CMD#FF2000 prodos/CMD.BIN
cp PLASMA.SYSTEM#FF2000 prodos/PLASMA.SYSTEM.SYS
cp PLASMA16.SYSTEM#FF2000 prodos/PLASMA16.SYSTEM.SYS
mkdir prodos/sys
cp ARGS#FE1000 prodos/sys/ARGS.REL
cp CONIO#FE1000 prodos/sys/CONIO.REL
cp DGR#FE1000 prodos/sys/DGR.REL
cp DHCP#FE1000 prodos/sys/DHCP.REL
cp ED#FE1000 prodos/sys/ED.REL
cp ETHERIP#FE1000 prodos/sys/ETHERIP.REL
cp FIBER#FE1000 prodos/sys/FIBER.REL
cp FILEIO#FE1000 prodos/sys/FILEIO.REL
cp FPSTR#FE1000 prodos/sys/FPSTR.REL
cp FPU#FE1000 prodos/sys/FPU.REL
cp INET#FE1000 prodos/sys/INET.REL
cp LONGJMP#FE1000 prodos/sys/LONGJMP.REL
cp MEMMGR#FE1000 prodos/sys/MEMMGR.REL
cp PORTIO#FE1000 prodos/sys/PORTIO.REL
cp SANE#FE1000 prodos/sys/SANE.REL
cp SDFAT#FE1000 prodos/sys/SDFAT.REL
cp SPIPORT#FE1000 prodos/sys/SPIPORT.REL
cp SNDSEQ#FE1000 prodos/sys/SNDSEQ.REL
cp UTHERNET#FE1000 prodos/sys/UTHERNET.REL
cp UTHERNET2#FE1000 prodos/sys/UTHERNET2.REL
cp ../sysfiles/FP6502.CODE#060000 prodos/sys/FP6502.CODE.BIN
cp ../sysfiles/ELEMS.CODE#060000 prodos/sys/ELEMS.CODE.BIN
mkdir prodos/demos
cp DGRTEST#FE1000 prodos/demos/DGRTEST.REL
cp RPNCALC#FE1000 prodos/demos/RPNCALC.REL
cp ROD#FE1000 prodos/demos/ROD.REL
mkdir prodos/demos/rogue
cp ROGUE#FE1000 prodos/demos/rogue/ROGUE.REL
cp ROGUECOMBAT#FE1000 prodos/demos/rogue/ROGUECOMBAT.REL
cp ROGUEIO#FE1000 prodos/demos/rogue/ROGUEIO.REL
cp ROGUEMAP#FE1000 prodos/demos/rogue/ROGUEMAP.REL
cp samplesrc/LEVEL0#040000 prodos/demos/rogue/LEVEL0.TXT
cp samplesrc/LEVEL1#040000 prodos/demos/rogue/LEVEL1.TXT
mkdir prodos/demos/sdutils
cp FATCAT#FE1000 prodos/demos/sdutils/FATCAT.REL
cp FATGET#FE1000 prodos/demos/sdutils/FATGET.REL
cp FATPUT#FE1000 prodos/demos/sdutils/FATPUT.REL
cp FATREADDSK#FE1000 prodos/demos/sdutils/FATREADDSK.REL
cp FATWRITEDSK#FE1000 prodos/demos/sdutils/FATWRITEDSK.REL
mkdir prodos/demos/net
cp HTTPD#FE1000 prodos/demos/net/HTTPD.REL
mkdir prodos/demos/music
cp PLAYSEQ#FE1000 prodos/demos/music/PLAYSEQ.REL
cp mockingboard/ultima3.seq prodos/demos/music/ULTIMA3.SEQ.BIN
mkdir prodos/bld
cp PLASM#FE1000 prodos/bld/PLASM.REL
cp CODEOPT#FE1000 prodos/bld/CODEOPT.REL
cp samplesrc/dgrtest.pla prodos/bld/DGRTEST.PLA.TXT
cp samplesrc/hello.pla prodos/bld/HELLO.PLA.TXT
cp samplesrc/fibertest.pla prodos/bld/FIBERTEST.PLA.TXT
cp samplesrc/mon.pla prodos/bld/MON.PLA.TXT
cp samplesrc/memtest.pla prodos/bld/MEMTEST.PLA.TXT
cp samplesrc/rod.pla prodos/bld/ROD.PLA.TXT
cp samplesrc/sieve.pla prodos/bld/SIEVE.PLA.TXT
cp samplesrc/test.pla prodos/bld/TEST.PLA.TXT
cp samplesrc/testlib.pla prodos/bld/TESTLIB.PLA.TXT
cp samplesrc/playseq.pla prodos/bld/PLAYSEQ.PLA.TXT
cp samplesrc/rpncalc.pla prodos/bld/RPNCALC.PLA.TXT
mkdir prodos/bld/inc
cp inc/args.plh prodos/bld/inc/ARGS.PLH.TXT
cp inc/cmdsys.plh prodos/bld/inc/CMDSYS.PLH.TXT
cp inc/conio.plh prodos/bld/inc/CONIO.PLH.TXT
cp inc/dgr.plh prodos/bld/inc/DGR.PLH.TXT
cp inc/fiber.plh prodos/bld/inc/FIBER.PLH.TXT
cp inc/fileio.plh prodos/bld/inc/FILEIO.PLH.TXT
cp inc/fpstr.plh prodos/bld/inc/FPSTR.PLH.TXT
cp inc/fpu.plh prodos/bld/inc/FPU.PLH.TXT
cp inc/inet.plh prodos/bld/inc/INET.PLH.TXT
cp inc/longjmp.plh prodos/bld/inc/LONGJMP.PLH.TXT
cp inc/memmgr.plh prodos/bld/inc/MEMMGR.PLH.TXT
cp inc/sane.plh prodos/bld/inc/SANE.PLH.TXT
cp inc/portio.plh prodos/bld/inc/PORTIO.PLH.TXT
cp inc/sdfat.plh prodos/bld/inc/SDFAT.PLH.TXT
cp inc/sndseq.plh prodos/bld/inc/SNDSEQ.PLH.TXT
cp inc/spiport.plh prodos/bld/inc/SPIPORT.PLH.TXT
cp inc/testlib.plh prodos/bld/inc/TESTLIB.PLH.TXT

View File

@ -0,0 +1,24 @@
//
// Test Fiber library
//
include "inc/cmdsys.plh"
include "inc/fiber.plh"
def fbrTest(fid, param)#0
byte i
for i = 1 to param
puth(fid); putc($0D)
fbrYield
next
end
//puts("fbrSwap = "); puth(@fbrSwap); putln
fbrInit(4)
fbrStart(@fbrTest, 3)
fbrStart(@fbrTest, 2)
fbrStart(@fbrTest, 1)
fbrYield; fbrYield; fbrYield; fbrYield
done

39
src/samplesrc/playseq.pla Normal file
View File

@ -0,0 +1,39 @@
include "inc/cmdsys.plh"
include "inc/args.plh"
include "inc/fileio.plh"
include "inc/sndseq.plh"
///////////////////////////////////////////////////////////////////////////////
//
// These are utility sequences/routines needed to test the music sequencer code.
//
word arg
word ref
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// More utility routines to test the getKey routine
//
// Sample background process
//
def backgroundProc#0
^$0400++
end
arg = argNext(argFirst)
if ^arg
ref = fileio:open(arg)
if ref
fileio:read(ref, heapmark(), heapavail())
fileio:close(ref)
musicPlay(heapmark(), TRUE)
getKey(@backgroundProc)
musicStop
else
puts("File not found.\n")
fin
fin
done
////////////////////////////////////////////////////////////////////////////////

View File

@ -1,7 +1,8 @@
include "inc/cmdsys.plh"
include "inc/conio.plh"
import rogueio
word rnd, getkb, home, gotoxy, tone
word rnd, getkb, tone
end
import roguemap
predef moveplayer
@ -138,28 +139,28 @@ export def fight(player, enemy)
word p_atck, e_atck
repeat
home()
gotoxy(0, 0)
conio:home()
conio:gotoxy(0, 0)
puts(player+name)
gotoxy(1, 2)
conio:gotoxy(1, 2)
puts("Skill :"); puti(player->skill)
gotoxy(1, 3)
conio:gotoxy(1, 3)
puts("Health :"); puti(player->health)
gotoxy(1, 4)
conio:gotoxy(1, 4)
puts("Energy :"); puti(player->energy)
gotoxy(20, 0)
conio:gotoxy(20, 0)
puts(entity[enemy->kind])
gotoxy(21, 2)
conio:gotoxy(21, 2)
puts("Power :"); puti(enemy->power)
gotoxy(21, 3)
conio:gotoxy(21, 3)
puts("Life :"); puti(enemy->life)
for e_atck = 0 to 9
gotoxy(0, 10 + e_atck)
conio:gotoxy(0, 10 + e_atck)
puts(@ascii_warrior + e_atck * 11)
gotoxy(20, 10 + e_atck)
conio:gotoxy(20, 10 + e_atck)
puts(ascii_entity[enemy->kind] + e_atck * 11)
next
gotoxy(12, 8); puts("F)ight or R)un?")
conio:gotoxy(12, 8); puts("F)ight or R)un?")
if toupper(getkb()) == 'R'
return 1
else

View File

@ -1,7 +1,5 @@
include "inc/cmdsys.plh"
const modkeep = $2000
const modinitkeep = $4000
include "inc/conio.plh"
byte[] initstr
byte = " ( )\n"
@ -22,10 +20,7 @@ word titlestr = @initstr
// Machine specific routines
//
export word rnd, getkb, home, gotoxy, tone
export word open, read, close, newline
byte noapple1 = "APPLE 1 NOT SUPPORTED."
export word rnd, getkb, tone
const ENV_REG = $FFDF
@ -35,8 +30,6 @@ const a2rndnum = $4E // ZP location of RND
const a2rndl = $4E
const a2rndh = $4F
word iobuff
word a3rndnum = 12345
def a3rnd
@ -60,7 +53,7 @@ def a2tone(duration, delay)
^SPEAKER
for i = 0 to delay
next
duration = duration - 1
duration--
loop
return 0
end
@ -75,183 +68,41 @@ def a3tone(duration, pitch)
return 0
end
//
// ProDOS file routines
//
def a2open(path, access)
byte params[6]
params.0 = 3
params:1 = path
params:3 = heapallocalign($0400, 8, @iobuff)
params.5 = 0
syscall($C8, @params)
return params.5
end
def a2close(refnum)
byte params[2]
if iobuff
heaprelease(iobuff)
iobuff = 0
fin
params.0 = 1
params.1 = refnum
return syscall($CC, @params)
end
def a2read(refnum, buff, len)
byte params[8]
params.0 = 4
params.1 = refnum
params:2 = buff
params:4 = len
params:6 = 0
syscall($CA, @params)
return params:6
end
def a2newline(refnum, emask, nlchar)
byte params[4]
params.0 = 3
params.1 = refnum
params.2 = emask
params.3 = nlchar
return syscall($C9, @params)
end
//
// SOS file routines
//
def a3open(path, access)
byte params[7]
params.0 = 4
params:1 = path
params.3 = 0
params:4 = @access
params.6 = 1
syscall($C8, @params)
return params.3
end
def a3close(refnum)
byte params[2]
params.0 = 1
params.1 = refnum
return syscall($CC, @params)
end
def a3read(refnum, buff, len)
byte params[8]
params.0 = 4
params.1 = refnum
params:2 = buff
params:4 = len
params:6 = 0
syscall($CA, @params)
return params:6
end
def a3newline(refnum, emask, nlchar)
byte params[4]
params.0 = 3
params.1 = refnum
params.2 = $FF
params.3 = nlchar
return syscall($C9, @params)
end
//
// Apple /// console routines
//
def dev_status(devnum, code, list)
byte params[5]
params.0 = 3
params.1 = devnum
params.2 = code
params:3 = list
return syscall($82, @params)
end
def a3keypressed
byte count
dev_status(cmdsys.devcons, 5, @count)
return count
end
def a3getkb
while not a3keypressed
while not conio:keypressed()
a3rndnum = a3rndnum + 123
loop
return getc()
end
def a3home
putc(28)
return 0
end
def a3gotoxy(ch, cv)
putc(24)
putc(ch)
putc(25)
putc(cv)
return 0
end
//
// Apple ][ console routines
//
def a2home
return call($FC58, 0, 0, 0, 0) // home()
end
def a2gotoxy(x, y)
^$24 = x + ^$20
return call($FB5B, y + ^$22, 0, 0, 0)
end
//
// Set machine specific routines
//
when MACHID & $C8
is $08 // Apple 1
puts(@noapple1)
puts("APPLE 1 NOT SUPPORTED.")
return -1
is $C0 // Apple ///
rnd = @a3rnd
getkb = @a3getkb
home = @a3home
gotoxy = @a3gotoxy
tone = @a3tone
open = @a3open
read = @a3read
close = @a3close
newline = @a3newline
break
otherwise // Apple ][
rnd = @a2rnd
getkb = @a2getkb
home = @a2home
gotoxy = @a2gotoxy
tone = @a2tone
open = @a2open
read = @a2read
close = @a2close
newline = @a2newline
wend
//
// Print title page
//
home()
conio:home()
while ^titlestr
puts(titlestr)
titlestr = titlestr + ^titlestr + 1

View File

@ -2,13 +2,15 @@
// Map module
//
include "inc/cmdsys.plh"
include "inc/conio.plh"
include "inc/fileio.plh"
import rogueio
const O_READ = 1
const O_WRITE = 2
const O_READ_WRITE = 3
word rnd, getkb, home, gotoxy, tone, open, read, close, newline
word rnd, getkb, tone
end
//
@ -175,14 +177,14 @@ export def loadmap(level)
// Set level map and read it
//
catacomb[catacomb] = '0' + level
mapref = open(@catacomb, O_READ)
mapref = fileio:open(@catacomb)
if mapref
newline(mapref, $7F, $0D)
fileio:newline(mapref, $7F, $0D)
for row = 1 to maprows - 2
l = read(mapref, map + (row << 6) + 1, mapcols)
l = fileio:read(mapref, map + (row << 6) + 1, mapcols)
^(map + (row << 6) + l) = WALL_TILE
next
close(mapref)
fileio:close(mapref)
return TRUE
fin
return FALSE
@ -260,7 +262,7 @@ export def drawmap(xorg, yorg, viewfield, viewdir, lightdist, viewdist)
//
// Clear screen
//
home()
conio:home()
//
// Draw background map if in light
//
@ -302,96 +304,96 @@ export def drawmap(xorg, yorg, viewfield, viewdir, lightdist, viewdist)
occluded = 1
when o & 7
is 0
//
// Run through lit octant beam points
//
//
// Run through lit octant beam points
//
for l = 1 to dbeam[lightdist]
//
// Check parent visiblity
//
//
// Check parent visiblity
//
if vispix[vbeam[l]]
imap = ((yorg - ybeam[l]) << rowshift) + xorg + xbeam[l]
tile = ^(map + imap)
if tile & OPAQUE_TILE
//
// The view stops here
//
//
// The view stops here
//
vispix[l] = 0
else
//
// This tile is transparent
//
//
// This tile is transparent
//
vispix[l] = 1
//
// Check adjacent tile for opaqueness - improves wall display
//
//
// Check adjacent tile for opaqueness - improves wall display
//
adjtile = ^(map + imap + 1) & INV_TILE
if adjtile & OPAQUE_TILE
^(viewmap + imap + 1) = adjtile | VIEWED_TILE
screen.[ycentr-ybeam[l], xcentr+xbeam[l]+1] = adjtile
fin
fin
fin
//
// Update view
//
//
// Update view
//
^(viewmap + imap) = tile | VIEWED_TILE
if tile <> PIT_TILE
if tile <> PIT_TILE
screen.[ycentr-ybeam[l], xcentr+xbeam[l]] = tile & INV_TILE
fin
fin
else
vispix[l] = 0
fin
next
//
// Run through visible octant beam points
//
//
// Run through visible octant beam points
//
for l = l to dbeam[viewdist]
//
// Check parent visiblity
//
//
// Check parent visiblity
//
if vispix[vbeam[l]]
imap = ((yorg - ybeam[l]) << rowshift) + xorg + xbeam[l]
tile = ^(map + imap)
if tile & OPAQUE_TILE
//
// The view stops here
//
//
// The view stops here
//
vispix[l] = 0
else
//
// This tile is transparent
//
//
// This tile is transparent
//
vispix[l] = 1
occluded = 0
occluded = 0
fin
//
// If the tile is in light, update view
//
if tile & LIT_TILE
//
// If the tile is in light, update view
//
if tile & LIT_TILE
^(viewmap + imap) = tile | VIEWED_TILE
screen.[ycentr-ybeam[l], xcentr+xbeam[l]] = tile & INV_TILE
darkness = 0
fin
fin
else
vispix[l] = 0
fin
//
// Advance beam distance
//
if l == dbeam[dist]
if occluded
//
// Beam fully occluded
// Advance beam distance
//
break
fin
//
// Update distance
//
if l == dbeam[dist]
if occluded
//
// Beam fully occluded
//
break
fin
//
// Update distance
//
occluded = 1
dist = dist + 1
fin
next
fin
next
break
is 1
for l = 1 to dbeam[lightdist]

View File

@ -1,4 +1,5 @@
include "inc/cmdsys.plh"
include "inc/conio.plh"
import roguemap
const xcentr = 20
@ -41,7 +42,7 @@ import roguecombat
end
import rogueio
word rnd, getkb, home, gotoxy, tone
word rnd, getkb, tone
end
const maxlight = 10
@ -200,24 +201,24 @@ end
//
def status#0
gotoxy(0, statusline)
conio:gotoxy(0, statusline)
puts(@helthstr)
puti(player.health)
gotoxy(9, statusline)
conio:gotoxy(9, statusline)
puts(@enrgystr)
puti(player.energy)
gotoxy(17, statusline)
conio:gotoxy(17, statusline)
puts(@oilstr)
puti(player:oil/10)
gotoxy(25, statusline)
conio:gotoxy(25, statusline)
puts(@goldstr)
puti(player.gold)
if player.raft
gotoxy(32, statusline)
conio:gotoxy(32, statusline)
puts(@raftstr)
fin
if player.key
gotoxy(36, statusline)
conio:gotoxy(36, statusline)
puts(@keystr)
fin
end
@ -461,7 +462,7 @@ def play
if player.health == 0
return FALSE
fin
gotoxy(xcentr, ycentr)
conio:gotoxy(xcentr, ycentr)
when toupper(getkb())
is 'I'
if totaldarkness
@ -598,7 +599,7 @@ def play
break
is 'X'
clearstatus
gotoxy(0, statusline)
conio:gotoxy(0, statusline)
puts(@quitstr)
if toupper(getkb()) == 'Y'
player.health = 0
@ -637,7 +638,7 @@ while loadmap(level)
totaldarkness = drawmap(player.xpos, player.ypos, player.fov, player.angle, player.lamp, maxview)
if not totaldarkness
drawentities
gotoxy(xcentr, ycentr)
conio:gotoxy(xcentr, ycentr)
putc(vplayer[player.angle])
fin
status
@ -647,7 +648,7 @@ while loadmap(level)
player.key = 0
level = level + 1
clearstatus
gotoxy(0, statusline)
conio:gotoxy(0, statusline)
if player.health == 0
break
fin