1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2026-03-12 01:41:40 +00:00

14 Commits
v2.20 ... ver-1

Author SHA1 Message Date
David Schmenk
7aaba1f9ef Update notes to 1.2 2018-05-11 06:17:28 -07:00
David Schmenk
a61da48e5d Update README.md 2018-05-11 06:16:07 -07:00
David Schmenk
45489b3ab8 Update image 2018-05-07 12:41:06 -07:00
David Schmenk
81e40c4c7f Fix unhandled IRQ on IIGS 2018-05-07 10:23:31 -07:00
David Schmenk
6ff3cc2673 Update images 2018-05-02 18:26:37 -07:00
David Schmenk
f964099c24 Stop continuous events when mouse button pressed 2018-05-02 13:27:58 -07:00
David Schmenk
54ecb3eec7 Generate mouse event on button state change 2018-05-02 13:18:00 -07:00
David Schmenk
bb97fe6353 Fix infunc for asm defs 2018-05-01 07:39:51 -07:00
David Schmenk
9e441de1c2 Version 1.2 with IRQ fixes and mouse module 2018-04-29 19:13:03 -07:00
David Schmenk
3d337f4fa8 Change TFTPD text file EOL strategy 2018-04-29 14:50:39 -07:00
David Schmenk
15e4013f97 Fix tftp xlte bug when writing text files. Bunp version 2018-04-29 07:25:43 -07:00
David Schmenk
5f1656b6a1 Backport some fixes from V2 2018-04-27 14:10:26 -07:00
David Schmenk
9809c2b5b2 Add TFTPD to release 1 2018-04-23 13:47:42 -07:00
David Schmenk
c0ddb5d197 Add TFTPD to release 1 2018-04-23 13:46:08 -07:00
16 changed files with 1187 additions and 22 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,5 @@
# 3/13/2018 PLASMA 1.1 Available!
[Download and read the Release Notes](https://github.com/dschmenk/PLASMA/blob/master/doc/Version%201.1.md)
# 4/29/2018 PLASMA 1.2 Available!
[Download and read the Release Notes](https://github.com/dschmenk/PLASMA/blob/master/doc/Version%201.2.md)
# The PLASMA Programming Language

View File

@@ -1,16 +1,16 @@
# PLASMA Version 1.1
# PLASMA Version 1.2
Welcome to PLASMA: the Grand Unifying Platform for the Apple 1, ][, and ///.
Download the four disk images (three if you don't plan to boot an Apple ///):
[PLASMA 1.1 System and ProDOS Boot](https://github.com/dschmenk/PLASMA/blob/master/PLASMA-SYS1.PO?raw=true)
[PLASMA 1.2 System and ProDOS Boot](https://github.com/dschmenk/PLASMA/blob/ver-1/PLASMA-SYS1.PO?raw=true)
[PLASMA 1.1 Build Tools](https://github.com/dschmenk/PLASMA/blob/master/PLASMA-BLD1.PO?raw=true)
[PLASMA 1.2 Build Tools](https://github.com/dschmenk/PLASMA/blob/ver-1/PLASMA-BLD1.PO?raw=true)
[PLASMA 1.1 Demos](https://github.com/dschmenk/PLASMA/blob/master/PLASMA-DEM1.PO?raw=true)
[PLASMA 1.2 Demos](https://github.com/dschmenk/PLASMA/blob/ver-1/PLASMA-DEM1.PO?raw=true)
[PLASMA 1.1 Apple /// SOS Boot ](https://github.com/dschmenk/PLASMA/blob/master/PLASMA-SOS1.PO?raw=true)
[PLASMA 1.2 Apple /// SOS Boot ](https://github.com/dschmenk/PLASMA/blob/ver-1/PLASMA-SOS1.PO?raw=true)
PLASMA can be run from floppies, System in Drive 1, and Build or Demos in Drive 2. Mass storage is the recommended installation that looks like (replacing HARDISK with your volume name of choice):
@@ -98,6 +98,16 @@ There is a [YouTube playlist](https://www.youtube.com/playlist?list=PLlPKgUMQbJ7
- The documentation is sparse and incomplete. Yep, could use your help...
# Changes in PLASMA for 1.2
1. Add TFTPD TFTP server
2. Fix Uthernet 1 driver
3. Add mouse module
4. Fix IRQ issues for interrupt driven mouse driver
# Changes in PLASMA for 1.1
1. All known bugs are fixed

32
src/inc/mouse.plh Normal file
View File

@@ -0,0 +1,32 @@
import mouse
//
// Status bits
//
const BUTTON_DOWN = $80
const BUTTON_LAST_DOWN = $40
const MOUSE_MOVED = $20
const VBL_INT = $08
const BUTTON_INT = $04
const MOVE_INT = $02
//
// Mode bits
//
const VBL_INT_ENABLE = $08
const BUTTON_INT_ENABLE= $04
const MOVE_INT_ENABLE = $02
const MOUSE_ENABLE = $01
//
// Mouse API
//
struc t_mouse
word chkVBL
word chkMouse
word readMouse // readMouse()#3
word setMouse // setMouse(mode)
word clearMouse
word posMouse // posMouse(x, y)
word clampMouse // clampMouse(xMin, xMax, yMin, yMax)
word homeMouse
word detachMouse
end
end

583
src/libsrc/apple/mouse.pla Normal file
View File

@@ -0,0 +1,583 @@
include "inc/cmdsys.plh"
//
// Mouse driver interface
//
predef chkVbl, chkMouse, readMouse#3, setMouse(mode), clearMouse, posMouse(x, y), clampMouse(xMin, xMax, yMin, yMax), homeMouse, detachMouse
word = @chkVbl, @chkMouse, @readMouse, @setMouse, @clearMouse, @posMouse, @clampMouse, @homeMouse, @detachMouse
word rom
byte params[]
byte slot, index, page
word setMouseFW
byte vblDiv, vblInt, mouInt, bttnPrev
asm equates
!SOURCE "vmsrc/plvmzp.inc"
end
//
// Serve Mouse/VBL IRQ
//
asm serviceMouse#0
VBLINT = $400 ; DUMMY VALUES TO BE FIXED-UP
MOUINT = $401
LASTBTTN= $402
CLD
JSR $C400
BCC +
RTS ; NOT MOUSE INT
+ LDY $0778+4 ; CHECK MOUSE INT CAUSE
TYA ; WAS IT VBL?
AND #$08
BEQ + ; NOPE, MOVE OR BUTTON
end
asm vblEvent
INC VBLINT ; INC VBL EVENT
+ TYA ; MOUSE MOVE OR BUTTON ACTIVE
AND #$82
end
asm bttnEvent
EOR LASTBTTN
BEQ +
end
asm mouseEvent
INC MOUINT ; INC MOUSE EVENT
+ TYA
AND #$80
end
asm updateBttn
STA LASTBTTN
end
asm updateMouse
LDX #$C4
LDY #$40
JMP $C400 ; IIGS REQUIRES THIS HAPPEN IN IRQ
end
asm serviceCYA#0
CLC
RTS
end
//
// Check for VBL (timer) and Mouse events (atomic read and reset)
//
asm chkEvt(addr)
LDA ESTKL,X
STA ESTKH-1,X
SEI
LDA (ESTKH-1,X) ; READ INT COUNT
TAY
LDA #$00
STA (ESTKH-1,X) ; CLEAR INT COUNT
CLI
STY ESTKL,X ; RETURN INT COUNT
STA ESTKH,X
RTS
end
asm readMouse#3
LDY #$04
DEX
DEX
DEX
PHP
SEI
LDA $0478,Y
STA ESTKL+2,X
LDA $0578,Y
STA ESTKH+2,X
LDA $04F8,Y
STA ESTKL+1,X
LDA $05F8,Y
STA ESTKH+1,X
LDA $0778,Y
STA ESTKL,X
LDA #$00
STA ESTKH,X
PLP
RTS
end
//
// Convert VBL interrupts into millisecond timer increment
//
def chkVblTimer
byte count
word msec
msec = 0
count = chkEvt(@vblInt)
while count
if vblDiv & 2
msec = msec + 16
vblDiv = 0
else
msec = msec + 17
vblDiv++
fin
count--
loop
return msec
end
//
// Check for VBL/Mouse interrupt events
//
def chkVbl
return chkEvt(@vblInt)
end
def chkMouse
return chkEvt(@mouInt)
end
//
// Mouse routines
//
def setMouse(mode)
return call(setMouseFW, mode, slot, page, $04)
end
def clearMouse
return call(rom + rom->$15, $00, slot, page, $04) // clearMouseFW
end
def posMouse(x, y)
//
// Fill screen holes
//
^($0478 + index) = x
^($0578 + index) = x >> 8
^($04F8 + index) = y
^($05F8 + index) = y >> 8
return call(rom + rom->$16, $00, slot, page, $04) // posMouseFW
end
def clampMouse(xMin, xMax, yMin, yMax)
^$0478 = xMin
^$0578 = xMin >> 8
^$04F8 = xMax
^$05F8 = xMax >> 8
call(rom + rom->$17, $00, slot, page, $04) // clampMouseFW
^$0478 = yMin
^$0578 = yMin >> 8
^$04F8 = yMax
^$05F8 = yMax >> 8
return call(rom + rom->$17, $01, slot, page, $04)) // clampMouseFW
end
def homeMouse
return call(rom + rom->$18, $00, slot, page, $04) // homeMouseFW
end
//
// Detach mouse from interrupts
//
def detachMouse
setMouse(0)
params.0 = 1
params.1 = 0
return syscall($41, @params)
end
//
// Identify Mouse card/slot and initialize
//
for rom = $C100 to $C700 step $0100
if rom->5 == $38 and rom->7 == $18 and rom->11 == $01 and rom->12 == $20
puts("Found Mouse in slot #"); putc('0' + ((rom >> 8) & $07)); putln
//
// Hook mouse IRQ handler into ProDOS IRQ chain
//
params.0 = 2
params.1 = 0
params:2 = @serviceMouse
syscall($40, @params)
//
// Hook CYA IRQ handler into ProDOS IRQ chain
//
params.0 = 2
params.1 = 3
params:2 = @serviceCYA
syscall($40, @params)
//
// Set values
//
slot = rom >> 8
index = slot & $07
page = index << 4
setMouseFW = rom + rom->$12
//
// Fix-up IRQ routine
//
serviceMouse:2 = rom + rom->$13 // serveMouseFW
serviceMouse:8 = $0778+index
vblEvent:1 = @vblInt
bttnEvent:1 = @bttnPrev
mouseEvent:1 = @mouInt
updateBttn:1 = @bttnPrev
updateMouse.1 = slot
updateMouse.3 = page
updateMouse:5 = rom + rom->$14 // readMouseFW
readMouse.1 = index
call(rom + rom->$19, $00, slot, page, $04) // initMouseFW
return modkeep
fin
next
//
// Not found
//
return -1
done
What follows is the relevant parts to the mouse driver for VM02
CHKMOUSE: LDX #$20 ; LOOK FOR MOUSE
LDA #$01
JSR SCAN_SLOTS
BCS NOMOUSE
PHA ; SAVE SLOT
LDY #$13
LDA (TMPTR),Y
STA SERVEMOUSE+1 ; FIXUP IRQ HANDLER
STX SERVEMOUSE+2
LDY #$14
LDA (TMPTR),Y
STA READMOUSE+1 ; FIXUP IRQ HANDLER
STX READMOUSE+2
TXA
AND #$07
STA MOUSE_SLOT
TAY
JSR MOUSE_INIT ; MAKE SURE MOUSE IS OFF, INTS OFF
LDA WARM_INIT
BNE :+
JSR PUTS
.ASCIIZ "Mouse in slot #"
LDA MOUSE_SLOT
JSR PRBYTE
JSR CROUT
: PLA
TAY
LDA #<MOUSE_DRIVER ; LOAD MOUSE DRIVER
LDX #>MOUSE_DRIVER
JSR LOAD_DRIVER
;
; SCAN SLOTS FOR MATCHING CARD ID
; ENTRY: A = START SLOT SCAN
; X = CARD ID
; EXIT: A = SLOT # :: C = 0
; X = SLOT PAGE
;
SCAN_SLOTS: ORA #$C0
STA TMPTR+1
LDA #$00
STA TMPTR
CHKSIG: LDY #$05
LDA (TMPTR),Y
CMP #$38 ; LOOK FOR PASCAL COMPAT SIG
BNE :+
LDY #$07
LDA (TMPTR),Y
CMP #$18
BNE :+
LDY #$0B
LDA (TMPTR),Y
CMP #$01
BNE :+
LDY #$0C
TXA ; LOOK FOR MATCHING ID
CMP (TMPTR),Y
BNE :+
LDA TMPTR+1
TAX
AND #$07
CLC
RTS
: INC TMPTR+1
LDA TMPTR+1
CMP #$C8
BCC CHKSIG
SEC
RTS
;*
;* TURN VBL INTS ON AFTER INIT
;*
VBL_INIT: LDA MOUSE_SLOT
BEQ NOVBL
ASL
TAX
LSR
ORA #MOUSECTL_CALLFW
TAY
SEI ; TURN OFF INTERRUPTS
LDA LINK_DEVCTRL,X
STA CALLVBLPROC+1
LDA LINK_DEVCTRL+1,X
STA CALLVBLPROC+2
LDA #$08 ; TURN MOUSE OFF, LEAVE VBL ON
LDX #$12
CALLVBLPROC: JSR $0000
CLI ; BACK ON
LDA WARM_INIT
BNE NOVBL
JSR PUTSLN
.ASCIIZ "VBlank timer active"
NOVBL: RTS
JSR PRODOS
.BYTE $40 ; ALLOC INTERRUPT
.ADDR ALLOCINTPARMS
.IFDEF DEBUG
BCC :+
JSR PUTSLN
.ASCIIZ "FAILED TO ALLOCATE INTERRUPT"
:
.ENDIF
RTS
ALLOCINTPARMS: .BYTE $02
.BYTE $00 ; INT NUM
.ADDR IO_INTERRUPT ; INT CODE
;*
;* I/O INTERRUPT ROUTINE
;*
IO_INTERRUPT: CLD
LDY #$02 ; SLOT #1 * 2
FNDIRQPROC: LDA LINK_DEVIRQ+1,Y
BEQ NXTIRQPROC
STA CALLIRQPROC+2
LDA LINK_DEVIRQ,Y
STA CALLIRQPROC+1
TYA
LSR
PHA
CALLIRQPROC: JSR $0000
BCS :+
PLA
TAY
PHA
JSR THREAD_NOTIFYIO
: PLA
ASL
TAY
NXTIRQPROC: INY
INY
CPY #$10
BCC FNDIRQPROC
CLC
RTS
;*
;* MOUSE DEVICE DRIVER
;*
MOUSE_INIT: ORA #$C0
STA XREGMOUSE1+1
STA XREGMOUSE2+1
ASL
ASL
ASL
ASL
STA YREGMOUSE1+1
STA YREGMOUSE2+1
LDA #$00
PHA ; DISABLE ALL MOUSE INTS
LDX #$12 ; FW INDEX FOR SETMOUSE
BNE CALLMOUSEFW
MOUSE_DRIVER:
MOUSE_DRVR_SZ: .WORD MOUSE_DRVR_END - MOUSE_DRVR_START
MOUSE_READ_OFS: .WORD MOUSE_READ - MOUSE_DRVR_START
MOUSE_WRITE_OFS: .WORD MOUSE_WRITE - MOUSE_DRVR_START
MOUSE_CTRL_OFS: .WORD MOUSE_CTRL - MOUSE_DRVR_START
MOUSE_IRQ_OFS: .WORD MOUSE_IRQ - MOUSE_DRVR_START
MOUSE_DRVR_START:
MOUSE_READ:
MOUSE_WRITE: SEC
RTS
MOUSE_X: .WORD $0000
MOUSE_Y: .WORD $0000
MOUSE_STATUS: .BYTE $00
MOUSE_CTRL: PHA
TYA
AND #$F8 ; MASK OFF SLOT #
CMP #MOUSECTL_CALLFW
BNE :+
CALLMOUSEFW: STX OPADDR
XREGMOUSE2: LDX #$C4
STX OPADDR+1
LDY #$00
LDA (OPADDR),Y ; GET ENTRYPOINT OFFSET
STA OPADDR
YREGMOUSE2: LDY #$40
PLA
SEI
JMP (OPADDR) ; CALL FIXED UP FUNCTION POINTER
: CMP #MOUSECTL_READMOUSE ; COPY MOUSE STATUS/POSITION INTO EASILY ACCESSIBLE MEMORY
BNE :+
PLA
TYA
AND #$07
TAX ; SAVE MOUSE PARAMETERS
ASL
TAY
LDA LINK_DEVREAD,Y
STA TMPTR
LDA LINK_DEVREAD+1,Y
STA TMPTR+1
SEI
LDY #$02
LDA $0478,X
STA (TMPTR),Y
PHA
INY
LDA $0578,X
STA (TMPTR),Y
INY
LDA $04F8,X
STA (TMPTR),Y
PHA
INY
LDA $05F8,X
STA (TMPTR),Y
INY
LDA $0778,X
STA (TMPTR),Y
STA TMP
PLA
TAY
PLA
TAX
LDA TMP
RTS
: CMP #MOUSECTL_CLAMPX
BEQ :+
CMP #MOUSECTL_CLAMPY
BNE :++
: PLA
STA $04F8
STX $05F8
LDA #$00
STA $0478
STA $0578
TYA
LSR
LSR
LSR
AND #$01
PHA
LDX #$17 ; FW INDEX FOR CLAMPMOUSE
BNE CALLMOUSEFW
SETMOUSE: PHA
LDX #$12 ; FW INDEX FOR SETMOUSE
BNE CALLMOUSEFW
: PLA
TYA
AND #$F8 ; MASK OFF SLOT #
CMP #IOCTL_OPEN
BNE :+
LDA #<THREAD_YIELD ; REMOVE SOFTWARE TIMER
STA LINK_YIELD
LDA #>THREAD_YIELD
STA LINK_YIELD+1
LDA #$0F ; TURN MOUSE INTS ON
BNE SETMOUSE
: CMP #IOCTL_CLOSE
BNE :+
LDA #$08 ; TURN MOUSE OFF
BNE SETMOUSE
: CMP #IOCTL_DEACTIVATE
BNE :+
LDA #MOUSECTL_NOIRQ
: CMP #MOUSECTL_NOIRQ ; UNINSTALL IRQ HANDLER
BNE :+
SEI
LDA #<SW_TIMER ; RE-INSTALL SW TIMER
STA LINK_YIELD
LDA #>SW_TIMER
STA LINK_YIELD+1
BNE SETMOUSE
: CMP #IOCTL_ID
BEQ :+
SEC
RTS
: LDA #$20 ; MOUSE ID
CLC
RTS
;
; VBLANK TIMER AND MOUSE IRQ
;
MOUSE_IRQ: STA TMP
SERVEMOUSE: JSR $C400
BCS VBLEXIT ; NOT MOUSE INT
LDY TMP ; CHECK MOUSE INT CAUSE
LDA $0778,Y
PHA
AND #$08 ; WAS IT VLB?
BEQ MOUSEEXIT ; NOPE, MOVE OR BUTTON
VBLTIC: LDX #$00
LDA #$11 ; 17 MSEC (2/3 OF THE TIME)
DEC TIMERADJUST
BNE :+
LDA #$02
STA TIMERADJUST
LDA #$10 ; 16 MSEC (1/3 OF THE TIME)
: JSR SYSTEM_TIC
MOUSEEXIT: PLA
AND #$86 ; MOUSE MOVE OR BUTTON ACTIVE
BEQ VBLEXIT
XREGMOUSE1: LDX #$C4
YREGMOUSE1: LDY #$40
READMOUSE: JSR $C400 ; IIGS REQUIRES THIS HAPPEN IN IRQ
CLC
RTS
VBLEXIT: SEC
RTS
MOUSE_DRVR_END EQU *
package apple2;
/*
* This class interfaces directly with the mouse device driver.
*/
public class Mouse
{
static private int slot, mouseSlot, mouseCtrl, ctrlRead, addrXPos, addrYPos;
static public int xPos, yPos, status;
public static boolean enable()
{
//
// Search for mouse card and disable VBL interrupts
//
for (slot = 1; slot < 8; slot++)
{
int mouse = vm02.call((1 << 19), 0x90 + (slot << 1)); // ID device
if ((mouse & 0x010000FF) == 0x20) // CARRY clear == valid device IOCTL, 0x20 == mouse card ID
{
mouseCtrl = 0x90 + (slot << 1);
mouseSlot = slot << 16;
ctrlRead = mouseSlot | 0x801400;
addrXPos = vm02.peekWord(0x0370 + (slot << 1)) + 2;
addrYPos = addrXPos + 2;
return (vm02.call(mouseSlot | (3 << 19), mouseCtrl) & 0x01000000) == 0; // open port
}
}
slot = 0;
return false;
}
public static void disable()
{
vm02.call(mouseSlot | (4<<19), mouseCtrl); // close port
}
public static void disableIRQ()
{
int vblSlot, vbl;
//
// Search for mouse card and disable/remove interrupts
//
for (vblSlot = 1; vblSlot < 8; vblSlot++)
{
vbl = vm02.call((1 << 19), 0x90 + (vblSlot << 1)); // ID device
if ((vbl & 0x010000FF) == 0x20) // CARRY clear == valid device IOCTL, 0x20 == mouse card ID
{
vm02.call((vblSlot << 16) | (17 << 19), 0x90 + (vblSlot << 1)); // MOUSECTL_UNVBL
break;
}
}
}
public static int slotMask()
{
return (1 << slot);
}
public static void update()
{
status = vm02.call(ctrlRead, mouseCtrl) & 0xFF; // CALL_FW ReadMouse
xPos = vm02.peekWord(addrXPos);
yPos = vm02.peekWord(addrYPos);
}
}

View File

@@ -500,7 +500,7 @@ def etherServiceIP
lclport = swab(rxptr=>udp_dst)
for i = 1 to MAX_UDP_NOTIFIES
if port=>notify_port == lclport
port=>notify_func(@iphdr=>ip_src,swab(rxptr=>udp_src),rxptr+t_udphdr,swab(rxptr=>udp_len),port=>notify_parm)
port=>notify_func(@iphdr=>ip_src,swab(rxptr=>udp_src),rxptr+t_udphdr,swab(rxptr=>udp_len)-t_udphdr,port=>notify_parm)
break
fin
port = port + t_notify

View File

@@ -30,12 +30,14 @@ SNDSEQ = rel/apple/SNDSEQ\#FE1000
PLAYSEQ = rel/apple/PLAYSEQ\#FE1000
SANITY = rel/SANITY\#FE1000
RPNCALC = rel/RPNCALC\#FE1000
MOUSE = rel/apple/MOUSE\#FE1000
UTHERNET2 = rel/apple/UTHERNET2\#FE1000
UTHERNET = rel/apple/UTHERNET\#FE1000
ETHERIP = rel/ETHERIP\#FE1000
INET = rel/INET\#FE1000
DHCP = rel/DHCP\#FE1000
HTTPD = rel/HTTPD\#FE1000
TFTPD = rel/TFTPD\#FE1000
DGR = rel/apple/DGR\#FE1000
GRAFIX = rel/apple/GRAFIX\#FE1000
GFXDEMO = rel/apple/GFXDEMO\#FE1000
@@ -75,7 +77,7 @@ TXTTYPE = .TXT
#SYSTYPE = \#FF2000
#TXTTYPE = \#040000
apple: $(PLVMZP_APL) $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM802) $(PLVM03) $(CMD) $(PLASMAPLASM) $(CODEOPT) $(ARGS) $(MEMMGR) $(MEMTEST) $(FIBER) $(FIBERTEST) $(LONGJMP) $(ED) $(MON) $(SOS) $(ROD) $(SIEVE) $(UTHERNET2) $(UTHERNET) $(ETHERIP) $(INET) $(DHCP) $(HTTPD) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(GRAFIX) $(GFXDEMO) $(DGR) $(DGRTEST) $(FILEIO_APL) $(CONIO_APL) $(JOYBUZZ) $(PORTIO) $(SPIPORT) $(SDFAT) $(FATCAT) $(FATGET) $(FATPUT) $(FATWDSK) $(FATRDSK) $(SANE) $(FPSTR) $(FPU) $(SANITY) $(RPNCALC) $(SNDSEQ) $(PLAYSEQ)
apple: $(PLVMZP_APL) $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM802) $(PLVM03) $(CMD) $(PLASMAPLASM) $(CODEOPT) $(ARGS) $(MEMMGR) $(MEMTEST) $(FIBER) $(FIBERTEST) $(LONGJMP) $(ED) $(MON) $(SOS) $(ROD) $(SIEVE) $(MOUSE) $(UTHERNET2) $(UTHERNET) $(ETHERIP) $(INET) $(DHCP) $(HTTPD) $(TFTPD) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(GRAFIX) $(GFXDEMO) $(DGR) $(DGRTEST) $(FILEIO_APL) $(CONIO_APL) $(JOYBUZZ) $(PORTIO) $(SPIPORT) $(SDFAT) $(FATCAT) $(FATGET) $(FATPUT) $(FATWDSK) $(FATRDSK) $(SANE) $(FPSTR) $(FPU) $(SANITY) $(RPNCALC) $(SNDSEQ) $(PLAYSEQ)
-rm vmsrc/plvmzp.inc
c64: $(PLVMZP_C64) $(PLASM) $(PLVM) $(PLVMC64)
@@ -260,6 +262,14 @@ $(HTTPD): samplesrc/httpd.pla $(PLVM02) $(PLASM)
./$(PLASM) -AMOW < samplesrc/httpd.pla > samplesrc/httpd.a
acme --setpc 4094 -o $(HTTPD) samplesrc/httpd.a
$(TFTPD): samplesrc/tftpd.pla $(PLVM02) $(PLASM)
./$(PLASM) -AMOW < samplesrc/tftpd.pla > samplesrc/tftpd.a
acme --setpc 4094 -o $(TFTPD) samplesrc/tftpd.a
$(MOUSE): libsrc/apple/mouse.pla $(PLVM02) $(PLASM)
./$(PLASM) -AMOW < libsrc/apple/mouse.pla > libsrc/apple/mouse.a
acme --setpc 4094 -o $(MOUSE) libsrc/apple/mouse.a
$(UTHERNET): libsrc/apple/uthernet.pla $(PLVM02) $(PLASM)
./$(PLASM) -AMOW < libsrc/apple/uthernet.pla > libsrc/apple/uthernet.a
acme --setpc 4094 -o $(UTHERNET) libsrc/apple/uthernet.a

View File

@@ -25,6 +25,7 @@ cp rel/SANE#FE1000 prodos/sys/SANE.REL
cp rel/apple/SDFAT#FE1000 prodos/sys/SDFAT.REL
cp rel/apple/SPIPORT#FE1000 prodos/sys/SPIPORT.REL
cp rel/apple/SNDSEQ#FE1000 prodos/sys/SNDSEQ.REL
cp rel/apple/MOUSE#FE1000 prodos/sys/MOUSE.REL
cp rel/apple/UTHERNET#FE1000 prodos/sys/UTHERNET.REL
cp rel/apple/UTHERNET2#FE1000 prodos/sys/UTHERNET2.REL
cp rel/apple/SOS#FE1000 prodos/sys/SOS.REL
@@ -59,6 +60,7 @@ cp samplesrc/APPLE3.PIX#060000 prodos/demos/apple3/APPLE3.PIX.BIN
mkdir prodos/demos/net
cp rel/HTTPD#FE1000 prodos/demos/net/HTTPD.REL
cp samplesrc/index.html prodos/demos/net/INDEX.HTML.TXT
cp rel/TFTPD#FE1000 prodos/sys/TFTPD.REL
mkdir prodos/demos/music
cp rel/apple/PLAYSEQ#FE1000 prodos/demos/music/PLAYSEQ.REL
@@ -87,6 +89,7 @@ cp samplesrc/rogue.pla prodos/bld/ROGUE.PLA.TXT
cp samplesrc/rogue.map.pla prodos/bld/ROGUE.MAP.PLA.TXT
cp samplesrc/rogue.combat.pla prodos/bld/ROGUE.COMBAT.PLA.TXT
cp samplesrc/gfxdemo.pla prodos/bld/GFXDEMO.PLA.TXT
cp samplesrc/mousetest.pla prodos/bld/MOUSETEST.PLA.TXT
mkdir prodos/bld/inc
cp inc/args.plh prodos/bld/inc/ARGS.PLH.TXT
@@ -108,4 +111,5 @@ 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
cp inc/grafix.plh prodos/bld/inc/GRAFIX.PLH.TXT
cp inc/mouse.plh prodos/bld/inc/MOUSE.PLH.TXT
cp vmsrc/apple/plvmzp.inc prodos/bld/inc/PLVMZP.INC.TXT

View File

@@ -0,0 +1,22 @@
include "inc/cmdsys.plh"
include "inc/conio.plh"
include "inc/mouse.plh"
var count
var xPos, yPos, bttn
Mouse:clampMouse(0, 39, 0, 23)
Mouse:setMouse(VBL_INT_ENABLE|MOVE_INT_ENABLE|BUTTON_INT_ENABLE|MOUSE_ENABLE)
while ^$C000 < 128
if Mouse:chkMouse()
conio:gotoxy(xPos, yPos); putc(' ')
xPos, yPos, bttn = Mouse:readMouse()#3
conio:gotoxy(xPos, yPos); putc(bttn & BUTTON_DOWN ?? '+' :: '^')
fin
if Mouse:chkVBL()
^$400++
fin
loop
getc
Mouse:detachMouse()
done

503
src/samplesrc/tftpd.pla Normal file
View File

@@ -0,0 +1,503 @@
//
// TFTP Daemon
//
include "inc/cmdsys.plh"
include "inc/inet.plh"
include "inc/fileio.plh"
include "inc/conio.plh"
//
// TFTP values
//
const TFTP_PORT = 69
const TID_INC = $0010
const RRQ = $0100
const WRQ = $0200
const DATAPKT = $0300
const ACKPKT = $0400
const ERRPKT = $0500
struc t_errPkt
word errOp
word errCode
byte errStr[]
byte errStrNull
end
struc t_ackPkt
word ackOp
word ackBlock
end
struc t_datPkt
word datOp
word datBlock
byte datBytes[]
end
res[t_errPkt] tftpError = $00, $05, $00, $00, $00
res[t_ackPkt] tftpAck = $00, $04, $00, $00
//
// Current file operations
//
byte ref, type, , netscii, filename[256]
word aux, block
word buff, TID = $1001
word portTFTP, portTID
//
// Swap bytes in word
//
asm swab(val)
!SOURCE "vmsrc/plvmzp.inc"
LDA ESTKL,X
LDY ESTKH,X
STA ESTKH,X
STY ESTKL,X
RTS
end
//
// Translate 'in' value to 'out' value
//
asm xlat(in, out, buf, len)#0
INX
INX
INX
INX
LDA ESTKL-4,X
ORA ESTKH-4,X
BEQ XLATEX
LDA ESTKL-3,X
STA SRCL
LDA ESTKH-3,X
STA SRCH
LDA ESTKL-1,X
LDY ESTKL-4,X
BEQ XLATLP
INC ESTKH-4,X
LDY #$00
XLATLP CMP (SRC),Y
BNE +
LDA ESTKL-2,X
STA (SRC),Y
LDA ESTKL-1,X
+ INY
BNE +
INC DSTH
INC SRCH
+ DEC ESTKL-4,X
BNE XLATLP
DEC ESTKH-4,X
BNE XLATLP
XLATEX RTS
end
//
// Convert byte to two hex chars
//
def btoh(cptr, b)#0
byte h
h = ((b >> 4) & $0F) + '0'
if h > '9'
h = h + 7
fin
^cptr = h
cptr++
h = (b & $0F) + '0'
if h > '9'
h = h + 7
fin
^cptr = h
end
def hexByte(hexChars)
byte lo, hi
lo = toupper(^(hexChars + 1)) - '0'
if lo > 9
lo = lo - 7
fin
hi = toupper(^hexChars) - '0'
if hi > 9
hi = hi - 7
fin
return (hi << 4) | lo
end
def hexWord(hexChars)
return (hexByte(hexChars) << 8) | hexByte(hexChars + 2)
end
def mkProName(netName, proName)#3
byte n, l, ascii, proType
word proAux
proType = $02 // default to BIN
proAux = $0000 // default to 0
//
// Check for CiderPress style extension
//
for l = 0 to 255
if netName->[l] == 0; break; fin
next
ascii = toupper(netName->[l + 1]) == 'N' // Netscii mode
if l > 7 and ^(netName + l - 7) == '#'
proType = hexByte(netName + l - 6)
proAux = hexWord(netName + l - 4)
l = l - 7
fin
memcpy(proName + 1, netName, l)
^proName = l
return ascii, proType, proAux
end
def mkNetName(proName, netName)
word l, n
byte fileinfo[t_fileinfo]
if !fileio:getfileinfo(proName, @fileinfo)
//
// Scan backward looking for dir seperator
//
l = ^proName
for n = l downto 1
if ^(proName + n) == '/'
break
fin
next
memcpy(netName + 1, proName + 1 + n, l - n)
^netName = l - n + 7
//
// Build CiderPress style extension
//
n = netName + ^netName - 6
^n = '#'
btoh(n + 1, fileinfo.file_type)
btoh(n + 3, fileinfo.aux_type.1)
btoh(n + 5, fileinfo.aux_type)
else
//
// Error getting info on file
//
puts("Error reading "); puts(proName); putln
return -1
fin
return 0
end
def readUDP(ipsrc, portsrc, data, len, param)
word err
err = 0
when *data
is $0500 // Error
err = *data
is $0400 // Ack
if swab(data=>ackBlock) <> block
puts("RRQ: Out-of-sequence block\n")
err = $0800 // Out-of-sequence block
break
fin
if param == 512 // Size of initial read
param = fileio:read(ref, buff+datBytes, 512)
if type == $04 // TXT type
xlat($0D, $0A, buff+datBytes, param)
fin
block++
buff=>datBlock = swab(block)
iNet:sendUDP(portTID, ipsrc, portsrc, buff, t_datPkt + param)
fin
if err
tftpError:errCode = err
iNet:sendUDP(portTID, ipsrc, portsrc, @tftpError, t_errPkt)
fin
if param < 512 or err
//
// All done
//
iNet:closeUDP(portTID)
fileio:close(ref)
ref = 0
fin
break
otherwise
puts("TFTP: RRQ Unexpected packet opcode: $"); puth(*data); putln
wend
return 0
end
def writeUDP(ipsrc, portsrc, data, len, param)
word err
err = 0
when *data
is $0300 // Data packet
if swab(data=>datBlock) <> block
puts("WRQ: Out-of-sequence block\n")
err = $0800 // Out-of-sequence block
break
fin
len = len - t_datPkt
if type == $04 // TXT type
xlat($0A, $0D, data+datBytes, len)
fin
if fileio:write(ref, data+datBytes, len) <> len
puts("WRQ: File write error\n")
tftpError:errCode = $0300 // Disk full error
break
fin
if not err
tftpAck:ackBlock = swab(block)
block++
iNet:sendUDP(portTID, ipsrc, portsrc, @tftpAck, t_ackPkt)
else
tftpError:errCode = err
iNet:sendUDP(portTID, ipsrc, portsrc, @tftpError, t_errPkt)
fin
if len < 512 or err
//
// All done
//
iNet:closeUDP(portTID)
fileio:close(ref)
ref = 0
fin
break
otherwise
puts("WRQ: Unexpected packet opcode: $"); puth(*data); putln
wend
return 0
end
def servUDP(ipsrc, portsrc, data, len, param)
byte info[24]
when *data
is RRQ // Read request
//
// Initiate file read
//
if ref
//
// File already open and active
//
tftpError:errCode = $0300 // Allocation exceeded
iNet:sendUDP(portTFTP, ipsrc, portsrc, @tftpError, t_errPkt)
return 0
fin
//
// Extract filename
//
netscii, type, aux = mkProName(data + 2, @filename)
ref = fileio:open(@filename)
if not ref
puts("Error opening file: "); puts(@filename)
puts(", Error: "); putb(perr); putln
tftpError:errCode = $0100 // File not found
iNet:sendUDP(portTFTP, ipsrc, portsrc, @tftpError, t_errPkt)
return 0
fin
info.0 = $0A
info:1 = @filename
syscall($C4, @info)
type = info.4
puts("Reading file: "); puts(@filename); putln
TID = (TID + TID_INC) | $1000
block = 1
buff=>datBlock = swab(block)
len = fileio:read(ref, buff+datBytes, 512)
if type == $04 // TXT type
xlat($0D, $0A, buff+datBytes, 512)
fin
portTID = iNet:openUDP(TID, @readUDP, len)
iNet:sendUDP(portTID, ipsrc, portsrc, buff, t_datPkt + len)
break
is WRQ // Write request
//
// Initiate file write
//
if ref
//
// File already open and active
//
tftpError:errCode = $0300 // Allocation exceeded
iNet:sendUDP(portTFTP, ipsrc, portsrc, @tftpError, t_errPkt)
return 0
fin
//
// Extract filename
//
netscii, type, aux = mkProName(data + 2, @filename)
fileio:destroy(@filename)
if fileio:create(@filename, type, aux)
puts("Create file error: "); putb(perr); putln
fin
ref = fileio:open(@filename)
if not ref
puts("Error opening file: "); puts(@filename)
puts(", Error: "); putb(perr); putln
tftpError:errCode = $0200 // Access violation
iNet:sendUDP(portTFTP, ipsrc, portsrc, @tftpError, t_errPkt)
return 0
fin
puts("Writing file: "); puts(@filename); putln
TID = (TID + TID_INC) | $1000
block = 1
tftpAck:ackBlock = 0
portTID = iNet:openUDP(TID, @writeUDP, 0)
iNet:sendUDP(portTID, ipsrc, portsrc, @tftpAck, t_ackPkt)
break
otherwise
puts("TFTP: Server Unexpected packet opcode: $"); puth(*data); putln
wend
return 0
end
if !iNet:initIP()
return -1
fin
puts("TFTP Server Version 0.1\n")
portTFTP = iNet:openUDP(TFTP_PORT, @servUDP, 0)
//
// Alloc aligned file/io buffers
//
buff = heapalloc(t_datPkt + 512)
buff=>datOp = $0300 // Data op
//
// Service IP
//
repeat
iNet:serviceIP()
until conio:keypressed()
done
Experpts from: RFC 1350, TFTP Revision 2, July 1992
TFTP Formats
Type Op # Format without header
2 bytes string 1 byte string 1 byte
-----------------------------------------------
RRQ/ | 01/02 | Filename | 0 | Mode | 0 |
WRQ -----------------------------------------------
2 bytes 2 bytes n bytes
---------------------------------
DATA | 03 | Block # | Data |
---------------------------------
2 bytes 2 bytes
-------------------
ACK | 04 | Block # |
--------------------
2 bytes 2 bytes string 1 byte
----------------------------------------
ERROR | 05 | ErrorCode | ErrMsg | 0 |
----------------------------------------
Initial Connection Protocol for reading a file
1. Host A sends a "RRQ" to host B with source= A's TID,
destination= 69.
2. Host B sends a "DATA" (with block number= 1) to host A with
source= B's TID, destination= A's TID.
Error Codes
Value Meaning
0 Not defined, see error message (if any).
1 File not found.
2 Access violation.
3 Disk full or allocation exceeded.
4 Illegal TFTP operation.
5 Unknown transfer ID.
6 File already exists.
7 No such user.
Internet User Datagram Header [2]
(This has been included only for convenience. TFTP need not be
implemented on top of the Internet User Datagram Protocol.)
Format
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Values of Fields
Source Port Picked by originator of packet.
Dest. Port Picked by destination machine (69 for RRQ or WRQ).
Length Number of bytes in UDP packet, including UDP header.
Checksum Reference 2 describes rules for computing checksum.
(The implementor of this should be sure that the
correct algorithm is used here.)
Field contains zero if unused.
Note: TFTP passes transfer identifiers (TID's) to the Internet User
Datagram protocol to be used as the source and destination ports.
A transfer is established by sending a request (WRQ to write onto a
foreign file system, or RRQ to read from it), and receiving a
positive reply, an acknowledgment packet for write, or the first data
packet for read. In general an acknowledgment packet will contain
the block number of the data packet being acknowledged. Each data
packet has associated with it a block number; block numbers are
consecutive and begin with one. Since the positive response to a
write request is an acknowledgment packet, in this special case the
block number will be zero. (Normally, since an acknowledgment packet
is acknowledging a data packet, the acknowledgment packet will
contain the block number of the data packet being acknowledged.) If
the reply is an error packet, then the request has been denied.
In order to create a connection, each end of the connection chooses a
TID for itself, to be used for the duration of that connection. The
TID's chosen for a connection should be randomly chosen, so that the
probability that the same number is chosen twice in immediate
succession is very low. Every packet has associated with it the two
TID's of the ends of the connection, the source TID and the
destination TID. These TID's are handed to the supporting UDP (or
other datagram protocol) as the source and destination ports. A
requesting host chooses its source TID as described above, and sends
its initial request to the known TID 69 decimal (105 octal) on the
serving host. The response to the request, under normal operation,
uses a TID chosen by the server as its source TID and the TID chosen
for the previous message by the requestor as its destination TID.
The two chosen TID's are then used for the remainder of the transfer.
As an example, the following shows the steps used to establish a
connection to write a file. Note that WRQ, ACK, and DATA are the
names of the write request, acknowledgment, and data types of packets
respectively. The appendix contains a similar example for reading a
file.
1. Host A sends a "WRQ" to host B with source= A's TID,
destination= 69.
2. Host B sends a "ACK" (with block number= 0) to host A with
source= B's TID, destination= A's TID.
At this point the connection has been established and the first data
packet can be sent by Host A with a sequence number of 1. In the
next step, and in all succeeding steps, the hosts should make sure
that the source TID matches the value that was agreed on in steps 1
and 2. If a source TID does not match, the packet should be
discarded as erroneously sent from somewhere else. An error packet
should be sent to the source of the incorrect packet, while not
disturbing the transfer. This can be done only if the TFTP in fact
receives a packet with an incorrect TID. If the supporting protocols
do not allow it, this particular error condition will not arise.
The following example demonstrates a correct operation of the
protocol in which the above situation can occur. Host A sends a
request to host B. Somewhere in the network, the request packet is
duplicated, and as a result two acknowledgments are returned to host
A, with different TID's chosen on host B in response to the two
requests. When the first response arrives, host A continues the
connection. When the second response to the request arrives, it
should be rejected, but there is no reason to terminate the first
connection. Therefore, if different TID's are chosen for the two
connections on host B and host A checks the source TID's of the
messages it receives, the first connection can be maintained while
the second is rejected by returning an error packet.

View File

@@ -1589,6 +1589,7 @@ int parse_defs(void)
next_line();
} while (scantoken != END_TOKEN);
scan();
infunc = 0;
return (1);
}
return (scantoken == EOL_TOKEN);

View File

@@ -40,7 +40,7 @@ predef sext(a)#1, divmod(a,b)#2, execmod(modfile)#1
//
// Exported CMDSYS table
//
word version = $0110 // 01.10
word version = $0120 // 01.20
word syspath
word syscmdln
word = @execmod

View File

@@ -1,6 +1,5 @@
const MACHID = $BF98
const iobuffer = $0800
const databuff = $2000
const RELADDR = $1000
const symtbl = $0C00
const freemem = $0006
@@ -38,7 +37,7 @@ predef execmod(modfile)#1
//
// Exported CMDSYS table
//
word version = $0110 // 01.10
word version = $0120 // 01.20
word syspath
word syscmdln
word = @execmod
@@ -1260,9 +1259,9 @@ def volumes()#0
params.0 = 2
params.1 = 0
params:2 = databuff
params:2 = heap
perr = syscall($C5, @params)
strbuf = databuff
strbuf = heap
for i = 0 to 15
^strbuf = ^strbuf & $0F
if ^strbuf
@@ -1287,12 +1286,12 @@ def catalog(path)#0
fin
firstblk = 1
repeat
if read(refnum, databuff, 512) == 512
entry = databuff + 4
if read(refnum, heap, 512) == 512
entry = heap + 4
if firstblk
entrylen = databuff.$23
entriesblk = databuff.$24
filecnt = databuff:$25
entrylen = heap->$23
entriesblk = heap->$24
filecnt = heap=>$25
entry = entry + entrylen
fin
for i = firstblk to entriesblk
@@ -1372,7 +1371,7 @@ def resetmemfiles()#0
//
// Close all files
//
^$BFD8 = 0
^$BF94 = 0
close(0)
//
// Set memory bitmap
@@ -1390,7 +1389,7 @@ def execsys(sysfile)#0
striptrail(sysfile)
refnum = open(sysfile)
if refnum
len = read(refnum, databuff, $FFFF)
len = read(refnum, $2000, $FFFF)
resetmemfiles()
if len
strcpy(sysfile, $280)

View File

@@ -1447,6 +1447,7 @@ ICALX LDA ESTKL,X
PHP
PLA
STA PSR
SEI
STA ALTRDON
PLA
STA IPH

View File

@@ -29,7 +29,7 @@ predef execmod(modfile)#1
//
// Exported CMDSYS table
//
word version = $0110 // 01.10
word version = $0120 // 01.20
word syspath
word cmdlnptr
word = @execmod